基础实现
Method swizzling
(有些人也叫它方法交换)对绝大多数OC
开发者来说都是很熟悉但并没有太多应用场景的一个东西。但是了解它对于了解OC
底层runtime
有很好的帮助。而且可以绕过一个限制,实现某些特殊的功能。
使用Method swizzling
,写法是基本相同的。比如说要给UIApplication
的sendEvent:
方法做交换,创建一个UIApplication
的分类,在load
方法中实现。
Method sendEventMethod = class_getInstanceMethod([self class], @selector(sendEvent:));
Method sendNotifyEventMethod = class_getInstanceMethod([self class], @selector(my_sendEvent:));
method_exchangeImplementations(sendEventMethod, sendNotifyEventMethod);
然后再写一个你想用来替换系统sendEvent:
实现的方法,在这个例子中叫做my_sendEvent:
,然后再这个实现中调用自身。
- (void)my_sendEvent:(UIEvent *)event {
NSLog(@"send evnet before %@",event);
[self my_sendEvent:event];
NSLog(@"send event after %@",event);
}
按照日常写代码的经验,在方法中调用自己,这不写成死循环了吗。但在这里,却并不会形成循环。这是因为我们在上面调用了method_exchangeImplementations
方法,将方法名和方法的实现进行了交换,当系统调用sendEvent:
方法时,调用到了my_sendEvent:
的实现中,这时候我就可以做我想要的处理,并在这个方法中调用my_sendEvent:
方法,但其实调用的实现是系统的sendEvent:
,这样可以在完成系统实现的基础上完成了自己想要的逻辑。
- 在这个方法中打断点也可以看到
_cmd
是sendEvnet:
而不是my_sendEvent:
特殊写法
前几日在看到AFNetworking
源码的时候突然想起之前看的某篇博客说AFNetworking
中dataTask
的resume
方法好像也是经过方法交换的,想看一下它是怎么做的,是否是和我上面写的一样。于是就搜索了了一下method_exchangeImplementations
方法,发现AFN
并没有通过分类实现,于是我就看了下它的实现方法。
AFN
的源码看起来比较绕,但剥离方法的封装之后,看起来就简单多了。比如想要交换上述的sendEvent:
方法,并不需要写在分类中,而是随便写了一个类,在这个类内部处理。
+ (void)load {
Method mySendEvent = class_getInstanceMethod([self class], @selector(my_sendEvent:));
if(class_addMethod([UIApplication class], @selector(my_sendEvent:), method_getImplementation(mySendEvent), method_getTypeEncoding(mySendEvent))) {
Method originalMethod = class_getInstanceMethod([UIApplication class], @selector(sendEvent:));
Method swizzlingMethod = class_getInstanceMethod([UIApplication class], @selector(my_sendEvent:));
method_exchangeImplementations(originalMethod, swizzlingMethod);
}
}
- (void)my_sendEvent:(UIEvent *)event {
[self my_sendEvent:event];
}
与之前的写法大致相同,唯一不同的是,将自己的实现方法my_sendEvent:
写在了这个类中,并将这个方法用class_addMethod
添加给了UIApplication
。在自己的方法中打断点可以发现,这里的self
并不是这个类的对象,而是UIApplication
的对象。这个涉及到了OC
底层objc_msgSend
方法的原理,在这里就不细说了。