在 Objective-C 中,Method Swizzling 最安全、最标准的时机只有一个:在类的 +load 方法中执行。
1. 为什么是 +load?
+load 方法具有以下几个保证安全性的特质:
- 执行时机极早:
+load在类被加载到内存时立即调用,远早于main函数,更早于任何实例对象的创建。这保证了在 App 真正运行业务逻辑之前,方法已经“变脸”成功。 - 全局唯一性:每个类的
+load只有在类加载时被系统调用一次。 - 线程安全:Runtime 在执行
+load时会加锁,确保方法交换过程不会产生竞争条件。
2. 为什么不能用 +initialize?
很多人容易混淆这两个方法,但在 +initialize 中做 Swizzling 是极其危险的:
- 延迟性:
+initialize是在类接收到第一条消息时才调用的。如果你的 App 有多处地方同时访问这个类,可能会导致 Swizzling 过程中产生竞态。 - 继承陷阱:如果子类没有实现
+initialize,它会调用父类的+initialize。这会导致父类的 Swizzling 代码被执行多次,从而可能把方法“换回来”,导致拦截失效。
3. 标准的安全代码模板
一个生产级别的 Swizzling 应该遵循“先尝试添加,后替换”的逻辑,防止意外篡改父类的方法。
Objective-C
#import <objc/runtime.h>
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 1. 核心安全步骤:先尝试给当前类添加原 selector
// 目的是防止当前类没实现该方法(只是继承了父类),直接交换会导致父类方法被改
BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
// 如果添加成功,说明原类里本来没这个方法,现在添加了 swizzled 的实现
// 那么我们只需要把 swizzledSelector 指向原来的父类实现即可
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
// 如果添加失败,说明原类里已经有这个方法实现了,直接交换指针
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
4. 2026 年的高级安全注意事项
随着系统架构的演进,现在做 Swizzling 还需要注意以下几点:
4.1 避免在 Swift 类中盲目使用
如果你的类是纯 Swift 写的(不继承自 NSObject),它是没有 +load 方法的。即使继承自 NSObject,Swift 也不再允许重写 +load。
- 对策:在 Swift 中,建议在
AppDelegate或特殊的初始化单例中显式调用一个配置函数来触发 OC 写的 Swizzling 代码。
4.2 模块化与隐私权限
2026 年的 iOS 系统审核更加严格。如果你 Swizzling 了敏感的系统 API(如 CLLocationManager 或 PHPhotoLibrary),Apple 的静态扫描工具会非常容易标记你的 App。
- 建议:除非是做无埋点监控、性能检测(APM)或极特殊的框架底层修改,否则尽量使用 AOP(面向切面编程)库 或 协议代理(Delegate Proxy) 来替代 Swizzling。
4.3 性能敏感性
正如之前讨论的,Swizzling 会触发 Cache Flush。
- 后果:如果你在 App 运行中频繁执行 Swizzling(虽然很少见),会造成持续的性能抖动。
总结
最安全的时机是 +load。它利用了 Runtime 加载类的天然屏障,确保了交换过程的唯一性、原子性和全局覆盖性。