美文网首页
iOS 方法调配(method swizzling)

iOS 方法调配(method swizzling)

作者: Wougle | 来源:发表于2018-01-27 13:09 被阅读0次

    Method Swizzing是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Swizzling代码写到任何地方,但是只有在这段Method Swilzzling代码执行完毕之后互换才起作用。

    与选择子名称相对应的方法是可以在运行期被改变的,所以,我们可以不用通过继承类并覆写方法就能改变这个类本身的功能。

    那么如何在运行期改变选择子对应的方法呢? 答:通过操纵类的方法列表的IMP指针

    什么是类方法表?什么是IMP指针呢?

    类的方法列表会把选择子的名称映射到相关的方法实现上,使得“动态消息派发系统”能够据此找到应该调用的方法。这些方法均以函数指针的形式来表示,这些指针叫做IMP。例如NSString类的选择子列表:

    类方法表的映射

    有了这张表,OC的运行期系统提供的几个方法就能操纵它。开发者可以向其中增加选择子,也可以改变某选择子对应的方法实现,也可以交换两个选择子所映射到的指针以达到交换方法实现的目的。
    举个 :交换lowercaseString和uppercaseString方法的实现:

    Method originalMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
    Method swappedMethod = class_getInstanceMethod([NSString class],@selector(uppercaseString));
    
    method_exchangeImplementations(originalMethod, swappedMethod);
    

    这样一来,类方法表的映射关系就变成了下图:

    交换两个方法

    这时,如果我们调用lowercaseString方法就会实际调用uppercaseString的方法,反之亦然。

    然而! 在实际应用中,只交换已经存在的两个方法是没有太大意义的。我们应该利用这个特性来给既有的方法添加新功能:
    它的实现原理是:先通过分类增加一个新方法,然后将这个新方法和要增加功能的旧方法替换(旧方法名对应新方法的实现),这样一来,如果我们调用了旧方法,就会实现新方法了。

    例如:我们要在原有的lowercaseString方法中添加一条输出语句。
    步骤一:我们先将新方法写在NSString的分类里:

    @interface NSString (EOCMyAdditions)
    - (NSString*)eoc_myLowercaseString;
    @end
    
    
    @implementation NSString (EOCMyAdditions)
    
    - (NSString*)eoc_myLowercaseString {
         NSString *lowercase = [self eoc_myLowercaseString];//eoc_myLowercaseString方法会在将来方法调换后执行lowercaseString的方法
         NSLog(@"%@ => %@", self, lowercase);//输出语句,便于调试
         return lowercase;
    }
    @end
    

    这段代码看上去好像会陷入递归调用的死循环,但是其实这个方法是准备和lowercaseString方法互换的。所以在runtime里,eco_myLowercaseString选择子实际上对应的是原来的lowercaseString方法实现。
    步骤二:交换两个方法的实现(操纵调换IMP指针)

    Method originalMethod =
     class_getInstanceMethod([NSString class],
     @selector(lowercaseString));
    Method swappedMethod =
     class_getInstanceMethod([NSString class],
     @selector(eoc_myLowercaseString));
    
    method_exchangeImplementations(originalMethod, swappedMethod);
    

    这样一来,我们如果交换了lowercaseString和eoc_myLowercaseString的方法实现,那么在调用原来的lowercaseString方法后就可以输出新增的语句了。

    “NSString *string = @"ThIs iS tHe StRiNg";
    NSString *lowercaseString = [string lowercaseString];
    // Output: ThIs iS tHe StRiNg => this is the string”
    

    读Effective Objective-C 2.0 有感

    相关文章

      网友评论

          本文标题:iOS 方法调配(method swizzling)

          本文链接:https://www.haomeiwen.com/subject/tdsnaxtx.html