Swift如何让UIViewController在viewWillAppear/Disappear时打印子类类型
结论
可以实现,无需修改任何现有UIViewController子类的代码,基于Objective-C runtime的方法交换(Method Swizzling)机制即可全局插入生命周期打印逻辑。
实现方案
方法交换的核心逻辑是在运行时替换UIViewController生命周期方法的默认实现,插入自定义打印代码后再调用原有实现,所有子类默认继承该逻辑。
以下分别提供Swift和Objective-C的实现示例:
Swift 实现
- 给UIViewController添加扩展,实现方法交换逻辑和自定义生命周期方法:
import UIKit extension UIViewController { static func enableLifeCycleLog() { guard !isLoggingEnabled else { return } isLoggingEnabled = true // 交换viewWillAppear swapMethods( original: #selector(UIViewController.viewWillAppear(_:)), swizzled: #selector(UIViewController.swizzled_viewWillAppear(_:)) ) // 交换viewWillDisappear swapMethods( original: #selector(UIViewController.viewWillDisappear(_:)), swizzled: #selector(UIViewController.swizzled_viewWillDisappear(_:)) ) } private static var isLoggingEnabled = false private static func swapMethods(original: Selector, swizzled: Selector) { guard let originalMethod = class_getInstanceMethod(UIViewController.self, original), let swizzledMethod = class_getInstanceMethod(UIViewController.self, swizzled) else { return } method_exchangeImplementations(originalMethod, swizzledMethod) } @objc private func swizzled_viewWillAppear(_ animated: Bool) { swizzled_viewWillAppear(animated) print("🔵 [viewWillAppear] \(type(of: self)) 地址: \(Unmanaged.passUnretained(self).toOpaque())") } @objc private func swizzled_viewWillDisappear(_ animated: Bool) { swizzled_viewWillDisappear(animated) print("🔴 [viewWillDisappear] \(type(of: self)) 地址: \(Unmanaged.passUnretained(self).toOpaque())") } }
- 在App启动时触发方法交换,在
AppDelegate.application(_:didFinishLaunchingWithOptions:)中添加一行代码即可:
UIViewController.enableLifeCycleLog()
Objective-C 实现
- 创建UIViewController分类:
// UIViewController+LifeCycleLog.h #import <UIKit/UIKit.h> @interface UIViewController (LifeCycleLog) @end // UIViewController+LifeCycleLog.m #import "UIViewController+LifeCycleLog.h" #import <objc/runtime.h> @implementation UIViewController (LifeCycleLog) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 交换viewWillAppear Method originalViewWillAppear = class_getInstanceMethod(self, @selector(viewWillAppear:)); Method swizzledViewWillAppear = class_getInstanceMethod(self, @selector(ll_viewWillAppear:)); method_exchangeImplementations(originalViewWillAppear, swizzledViewWillAppear); // 交换viewWillDisappear Method originalViewWillDisappear = class_getInstanceMethod(self, @selector(viewWillDisappear:)); Method swizzledViewWillDisappear = class_getInstanceMethod(self, @selector(ll_viewWillDisappear:)); method_exchangeImplementations(originalViewWillDisappear, swizzledViewWillDisappear); }); } - (void)ll_viewWillAppear:(BOOL)animated { [self ll_viewWillAppear:animated]; NSLog(@"🔵 [viewWillAppear] %@ 地址: %p", NSStringFromClass([self class]), self); } - (void)ll_viewWillDisappear:(BOOL)animated { [self ll_viewWillDisappear:animated]; NSLog(@"🔴 [viewWillDisappear] %@ 地址: %p", NSStringFromClass([self class]), self); } @end
- 无需额外调用,分类的
load方法会在App启动时自动执行,打印逻辑全局生效。
注意事项
- 该逻辑仅可在Debug模式下使用,上线前必须移除,避免产生不必要的性能消耗和日志泄露。
- 若你的自定义子类重写了生命周期方法但未调用
super对应的方法,打印逻辑不会触发,补全super调用即可。 - 日志中添加了实例内存地址,可以更精准的追踪同一个实例的生命周期调用顺序,排查重复dismiss的问题更方便。
内容的提问来源于stack exchange,提问作者Timothy
相关产品推荐
相关产品推荐

