iOS - Method Swizzling

让我详细总结一下 Method Swizzling 的使用和注意事项:

1. 基本实现

objectivec 复制代码
// 基本的 Method Swizzling 实现
@implementation UIViewController (Tracking)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 1. 获取原始方法和替换方法
        Method originalMethod = class_getInstanceMethod(self, @selector(viewDidAppear:));
        Method swizzledMethod = class_getInstanceMethod(self, @selector(track_viewDidAppear:));
        
        // 2. 尝试添加方法实现
        BOOL didAddMethod = class_addMethod(self,
                                          @selector(viewDidAppear:),
                                          method_getImplementation(swizzledMethod),
                                          method_getTypeEncoding(swizzledMethod));
        
        // 3. 如果添加成功,直接替换实现
        if (didAddMethod) {
            class_replaceMethod(self,
                              @selector(track_viewDidAppear:),
                              method_getImplementation(originalMethod),
                              method_getTypeEncoding(originalMethod));
        } else {
            // 4. 添加失败,说明方法已存在,直接交换实现
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

// 新方法实现
- (void)track_viewDidAppear:(BOOL)animated {
    // 1. 调用原始实现
    [self track_viewDidAppear:animated];  // 注意:这里实际会调用原始的 viewDidAppear:
    
    // 2. 添加新的功能
    [self trackViewPageAppear];
}

@end

2. 注意事项

2.1 执行时机

objectivec 复制代码
// 1. 在 +load 方法中执行
+ (void)load {
    // 在这里执行 swizzling
    // 因为 +load 在类加载时调用,比较安全
}

// 2. 避免在 +initialize 中执行
+ (void)initialize {
    // 不要在这里执行 swizzling
    // 因为 +initialize 可能被调用多次
}

2.2 线程安全

objectivec 复制代码
// 使用 dispatch_once 确保只执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // swizzling 代码
});

2.3 方法命名

objectivec 复制代码
// 使用清晰的前缀避免命名冲突
- (void)xyz_swizzledMethod {
    // 新的实现
}

// 避免使用
- (void)swizzledMethod {  // 可能造成命名冲突
}

2.4 子类处理

objectivec 复制代码
// 检查当前类是否真正实现了该方法
+ (void)swizzleMethod:(SEL)originalSelector {
    Class class = [self class];
    
    // 确保方法存在于当前类,而不是继承自父类
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    if (method_getImplementation(originalMethod) == method_getImplementation(class_getInstanceMethod([NSObject class], originalSelector))) {
        return;  // 方法来自基类,不进行 swizzling
    }
    
    // 进行 swizzling
}

2.5 方法签名

objectivec 复制代码
// 确保方法签名匹配
- (void)swizzled_method:(id)arg1 withObject:(id)arg2 {
    // 参数类型和数量必须与原方法完全匹配
}

3. 常见陷阱

3.1 递归调用

objectivec 复制代码
// 错误示例
- (void)swizzled_method {
    [self swizzled_method];  // 死循环!
}

// 正确示例
- (void)swizzled_method {
    // 在 swizzling 后,这里会调用原始实现
    [self swizzled_method];  
    // 添加新功能
}

3.2 父类方法

objectivec 复制代码
// 避免重复 swizzling
static BOOL isSwizzled = NO;

+ (void)load {
    if (!isSwizzled) {
        // 执行 swizzling
        isSwizzled = YES;
    }
}

4. 最佳实践

4.1 封装 Swizzling

objectivec 复制代码
@implementation NSObject (Swizzling)

+ (BOOL)swizzleMethod:(SEL)origSel withMethod:(SEL)altSel {
    Method origMethod = class_getInstanceMethod(self, origSel);
    Method altMethod = class_getInstanceMethod(self, altSel);
    
    if (!origMethod || !altMethod) {
        return NO;
    }
    
    class_addMethod(self,
                    origSel,
                    class_getMethodImplementation(self, origSel),
                    method_getTypeEncoding(origMethod));
    class_addMethod(self,
                    altSel,
                    class_getMethodImplementation(self, altSel),
                    method_getTypeEncoding(altMethod));
    
    method_exchangeImplementations(class_getInstanceMethod(self, origSel),
                                 class_getInstanceMethod(self, altSel));
    
    return YES;
}

@end

4.2 文档化

objectivec 复制代码
// 清晰的文档说明
/**
 * 替换 viewDidAppear: 方法用于追踪页面显示
 * 警告:此方法会影响所有 UIViewController 实例
 * 在 +load 方法中调用,确保在应用启动时完成替换
 */
+ (void)swizzleViewDidAppear {
    // swizzling 实现
}

总结:

  1. 在 +load 中执行
  2. 使用 dispatch_once
  3. 检查方法存在性
  4. 正确处理方法签名
  5. 避免递归调用
  6. 注意子类影响
  7. 清晰的命名约定
  8. 完善的文档说明

Method Swizzling 是一个强大但危险的特性,需要谨慎使用。

相关推荐
顺天认证3 小时前
环保之路:ISO14001环境管理体系认证的力量
ios
云卓SKYDROID18 小时前
工业级手持地面站(支持Android和IOS)技术详解!
android·ios·无人机·科普·高科技·云卓科技
打工人你好1 天前
iOS 逆向学习 - Inter-Process Communication:进程间通信
学习·ios
KillerNoBlood1 天前
IOS界面传值-OC
ios·objective-c·block·代理
打工人你好1 天前
iOS 逆向学习 - iOS Application Structure:iOS 应用程序结构
学习·ios
刘小哈哈哈1 天前
iOS layer.mask 的原理
ios
安和昂1 天前
Effective Objective-C 第一章阅读笔记
笔记·ios·objective-c
刘小哈哈哈1 天前
iOS手势shouldBeRequiredToFailByGestureRecognizer 机制
macos·ios·cocoa
家电修理师2 天前
HBuilderX打包ios保姆式教程
前端·ios