关于Method Swizzling

理解Method Swizzling是学习runtime机制的一个很好地机会

Method Swizzling是改变一个selector的实际实现的技术,通过这一技术,我们可以在运行时通过类的分类发表中selector对应的函数,来修改方法的实现

例如,我们想跟踪程序中每一个view controller战士给用户的次数:当然,我们可以在每个view controller的viewDidAppear中添加跟踪代码;但是这个泰国麻烦了,需要在每个view controller中写重复的代码,创建一个子类可能是一种实现方式,但需要同时创建UIViewController的子类,这也同样会产生许多重复的代码

#import <objc/runtime.h>
 
@implementation UIViewController (Tracking)
 
+ (void)load {
         static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         Class class = [self class];        
         // When swizzling a class method, use the following:
                     // Class class = object_getClass((id)self);
 
         SEL originalSelector = @selector(viewWillAppear:);
                     SEL swizzledSelector = @selector(xxx_viewWillAppear:);
 
         Method originalMethod = class_getInstanceMethod(class, originalSelector);
                     Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
 
         BOOL didAddMethod =
                         class_addMethod(class,
                 originalSelector,
                 method_getImplementation(swizzledMethod),
                 method_getTypeEncoding(swizzledMethod));
 
         if (didAddMethod) {
                         class_replaceMethod(class,
                 swizzledSelector,
                 method_getImplementation(originalMethod),
                 method_getTypeEncoding(originalMethod));
         } else {
             method_exchangeImplementations(originalMethod, swizzledMethod);
         }
     });
}
 
#pragma mark - Method Swizzling
 
- (void)xxx_viewWillAppear:(BOOL)animated {
         [self xxx_viewWillAppear:animated];
     NSLog(@"viewWillAppear: %@", self);
}
 
@end
我们通过method sizzling修改了UIViewController的@selector(viewWillAppear:)对应的函数指针,使其实现指向了我们自定义的xxx_viewWillAppear的实现,这样,当UIViewController及其子类的对象调用viewWillAppear时,都会打印一条日志信息
上面的例子很好的展示了使用method swizzling来一个类中注入一些我们新的操作。当然,还有许多场景可以使用method swizzling,在此不多距离,在此我们说说使用method swizzling需要注意的一些问题
Swizzling应该总是在+load中执行
在Objective-C中,运行时会自动嗲用每个类的两个方法,+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用,这两个方法是可选的,切只有在实现了它们是才会被调用,由于method swizzling会影响到类的全局状态,因此要尽量避免在并发处理中出现竞争的情况,+load能保证在类的初始化过程中被夹在,并保证这种改变应用级别的行为的一致性。相比之下,+initialize在其执行时不提供这种保证-事实上,如果在应用中没为给这个类发送消息,则它可能永远不会被调用
Swizzling应该总是在dispatch_once中执行
与上面相同,因为swizzling会改变全局状态,所iyiwomen需要在运行时采取一些预防措施,原子性就是这样一种措施,它确保代码只被执行一次,不管有多少个县城。GCD的dispatch_once可以确保这种行为,我们应该将其作为method swizzling的最佳实践。
选择器、方法与实现
在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一个方法,我们可以在分发表中将一个方法的现有的选择器映射到不同的实现,而将该选择器对应的原始实现关联到一个新的选择器中。
- (void)xxx_viewWillAppear:(BOOL)animated {
     [self xxx_viewWillAppear:animated];
     NSLog(@"viewWillAppear: %@", NSStringFromClass([self class]));
}
看上去是会导致无限循环的,但是实际上并没有,在swizzling的过程中,方法中得[self xxx_viewWillAppear:animated]已经被重新指定到UIViewController类的viewWillAppear:中。在这种情况下,不会产生无限循环。不过如果我们调用的是[self viewWillAppear:animated],则会产生无限循环,因为这个方法的实现在运行时已经被重新指定为xxx_viewWillAppear:了
注意事项
Swizzling通常被称作是一种黑魔法,容易产生不可预知的行为和无法预见的后果,虽然它不是最安全的,但是如果遵从以下几点预防措施的话,还是比较安全的:
1.总是调用方法的原始实现(除非有更好的理由不这么做):API提供了一个输入与输出约定,但其内部实现就是一个黑盒,Swizzle一个方法而不调用原始实现可能会打破私有状态底层操作,从而影响到程序的其他部分
2.避免冲突:给自定义的分类方法加前缀,从而使其与所依赖的代码库不会存在明明冲突
3.明白是怎么回事:简单地拷贝粘贴swizzle代码而不理解它是如何工作的,不仅危险,而且会浪费学习Objective-C运行时的机会,阅读Objective-C Runtime Reference和查看<objc/runtime.h>头文件以了解事件是如何发生的
4.小心操作:无论我们对Foundation,UIKit或其它内建框架执行Swizzle操作包邮多大信心,需要知道在下一版本中许多事可能会不一样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值