You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

运行时调用逻辑解析

  1. 方法交换的本质
    method_exchangeImplementations交换的是方法的实现(IMP,即函数指针),而非方法名(SEL)。交换完成后:
  • 当向UIViewController对象发送viewDidAppear这个SEL时,实际执行的是原来s_viewDidAppear的实现(打印"s_viewDidAppear"的代码)
  • 发送s_viewDidAppear这个SEL时,执行的是原来viewDidAppear的默认实现
  1. 子类调用super.viewDidAppear的过程
  • 编译阶段,super.viewDidAppear(animated)仅会被标记为:向父类(UIViewController)发送viewDidAppear这个SEL,不会直接绑定父类的原始实现。
  • 运行阶段,执行到这行代码时,系统会沿着继承链从UIViewController的方法表中查找viewDidAppear对应的IMP——此时这个IMP已经被替换成了s_viewDidAppear的实现,所以会先打印"s_viewDidAppear"。
  • 由于交换操作直接修改了UIViewController类的方法表,所有继承自它的子类在调用父类的viewDidAppear时,都会走到交换后的实现,因为子类没有重写该方法的话,会直接复用父类方法表中的记录。
  1. 预期不符的核心原因
    你误以为继承是编译时绑定,super.viewDidAppear会直接调用父类原有的viewDidAppear实现,但实际上OC/Swift的所有方法调用(包括super调用)都是运行时动态解析SEL对应的IMP,编译阶段只确定要发送的SEL,具体执行哪段代码完全由运行时的方法表状态决定。

内容的提问来源于stack exchange,提问作者Alfred Woo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 16:22:50