Method Swizzling与继承的运行时调用逻辑疑问
方法交换(Method Swizzling)后父类方法调用的运行时逻辑解析
你通过method_exchangeImplementations交换了UIViewController的viewDidAppear和自定义s_viewDidAppear方法后,子类SViewController重写viewDidAppear并调用super.viewDidAppear时,实际输出了s_viewDidAppear和SViewController.viewDidAppear,但预期仅打印后者。核心困惑点在于对OC/Swift运行时方法调用逻辑的误解,下面结合你的代码拆解整个过程:
代码示例
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let originalMethod = class_getInstanceMethod(UIViewController.self, #selector(UIViewController.viewDidAppear)) let swizzledMethod = class_getInstanceMethod(UIViewController.self, #selector(UIViewController.s_viewDidAppear)) if let originalMethod, let swizzledMethod { method_exchangeImplementations(originalMethod, swizzledMethod) } return true }
UIViewController+S.swift
extension UIViewController { func s_viewDidAppear(_ animated: Bool) { print("s_viewDidAppear") } }
SViewController.swift
class SViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) print("SViewController.viewDidAppear") } }
实际输出
s_viewDidAppear SViewController.viewDidAppear
运行时调用逻辑解析
- 方法交换的本质
method_exchangeImplementations交换的是方法的实现(IMP,即函数指针),而非方法名(SEL)。交换完成后:
- 当向
UIViewController对象发送viewDidAppear这个SEL时,实际执行的是原来s_viewDidAppear的实现(打印"s_viewDidAppear"的代码) - 发送
s_viewDidAppear这个SEL时,执行的是原来viewDidAppear的默认实现
- 子类调用
super.viewDidAppear的过程
- 编译阶段,
super.viewDidAppear(animated)仅会被标记为:向父类(UIViewController)发送viewDidAppear这个SEL,不会直接绑定父类的原始实现。 - 运行阶段,执行到这行代码时,系统会沿着继承链从
UIViewController的方法表中查找viewDidAppear对应的IMP——此时这个IMP已经被替换成了s_viewDidAppear的实现,所以会先打印"s_viewDidAppear"。 - 由于交换操作直接修改了
UIViewController类的方法表,所有继承自它的子类在调用父类的viewDidAppear时,都会走到交换后的实现,因为子类没有重写该方法的话,会直接复用父类方法表中的记录。
- 预期不符的核心原因
你误以为继承是编译时绑定,super.viewDidAppear会直接调用父类原有的viewDidAppear实现,但实际上OC/Swift的所有方法调用(包括super调用)都是运行时动态解析SEL对应的IMP,编译阶段只确定要发送的SEL,具体执行哪段代码完全由运行时的方法表状态决定。
内容的提问来源于stack exchange,提问作者Alfred Woo
相关产品推荐
相关产品推荐

