Objective-C Runtime 运行时之-Method Swizzling(下)

方法交换--Method Swizzling

上文介绍了Method Swizzling简单使用,这里将讲解严谨的写法;

问题一:为什么使用+ load 方法不使用 +initial 方法
答:

  1. +load会在类初始加载时调用
  2. +initialize会在第一次调用类的类方法或实例方法之前被调用
  3. +initialize并发的时候,可能两个方法同时调用;一个方法实现了方法交换,另外一个方法没有进行方法交换;原则上第一个拿到类的会进行交换,往后使用的都会交换;但是并发执行的,两个都拿到了,initialize只会去交换一个,不会去交换两个;所以放在initialize不好
  4. +load能保证在类的初始化过程中被加载,就能保证一定会交换方法

问题二:为什么在 dispatch_once 中执行
答:

  1. 因为是在全局改的;
  • 1.可能会涉及到多线程并发操纵
  • 2.可能涉及到重复调用(重复交换)
  1. 我们只希望修改一次,我们就使用 dispatch_once 执行一次

选择器、方法与实现

在Objective-C中,选择器(selector)、方法(method)和实现(implementation)是运行时中一个特殊点,虽然在一般情况下,这些术语更多的是用在消息发送的过程描述中。

以下是Objective-C Runtime Reference中的对这几个术语一些描述:

  • 1.Selector(typedef struct objc_selector *SEL):用于在运行时中表示一个方法的名称。一个方法选择器是一个C字符串,它是在Objective-C运行时被注册的。选择器由编译器生成,并且在类被加载时由运行时自动做映射操作。
  • 2.Method(typedef struct objc_method *Method):在类定义中表示方法的类型
  • 3.Implementation(typedef id (*IMP)(id, SEL, …)):这是一个指针类型,指向方法实现函数的开始位置。这个函数使用为当前CPU架构实现的标准C调用规范。每一个参数是指向对象自身的指针(self),第二个参数是方法选择器。然后是方法的实际参数。

理解这几个术语之间的关系最好的方式是:一个类维护一个运行时可接收的消息分发表;分发表中的每个入口是一个方法(Method),其中key是一个特定名称,即选择器(SEL),其对应一个实现(IMP),即指向底层C函数的指针。

为了swizzle一个方法,我们可以在分发表中将一个方法的现有的选择器映射到不同的实现,而将该选择器对应的原始实现关联到一个新的选择器中。

注意:交换方法原来方法有什么返回值,交换的方法返回值要一致;
1.比如原来返回是 instance,交换的方法返回值也是instance
2.比如原来返回是void,交换的方法返回值也是void

以下是 demo

#import "UIView+Tracking.h"
#import 

@implementation UIView (Tracking)

+(void)load{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        
        Class class = [self class];
        
        //0.获取 sel
        
        SEL swizllingSEL = @selector(cbw_setBackgroundColor:);
        SEL systemSEL = @selector(setBackgroundColor:);
        
        //1.获取方法列表--是对象方法还是实例方法
        
        Method swizllingMethod = class_getInstanceMethod(class, swizllingSEL);
        
        Method systemMethod = class_getInstanceMethod(class, systemSEL);
        
        //2.看方法有没有实现---imp
        BOOL didAddMethod =
        class_addMethod(class,
                        systemSEL,
                        method_getImplementation(swizllingMethod),
                        method_getTypeEncoding(swizllingMethod));
        
        //3.如果有实现,就交换.没有实现就不交换
        
        if (didAddMethod) {
            //repalce--没有交换系统的方法,来换一次
            class_replaceMethod(class, swizllingSEL, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
            
        }else{
            //exchange
            method_exchangeImplementations(swizllingMethod, systemMethod);
            
        }
        
    });
    
}

//分类中没有 super 父类这个东西
//-(void)viewWillAppear:(BOOL)animated{
//
//}

//系统里面调用: setBackgroundColor-(exchange) -->cbw_setBackgroundColor

//cbw_setBackgroundColor里面调用:cbw_setBackgroundColor -->setBackgroundColor

- (void)cbw_setBackgroundColor:(UIColor *)backgoroundColor{

      [self cbw_setBackgroundColor:[UIColor redColor]];
    //这里使用自己的方法--会调换成系统的方法
    NSLog(@"%s",__func__);
    
    NSLog(@"交换成功");

}

@end

参考资料

南峰子的技术博客-Objective-C Runtime 运行时之四:Method Swizzling
Method Swizzling

你可能感兴趣的:(Objective-C Runtime 运行时之-Method Swizzling(下))