Object-C对象收到消息后,究竟会调用何种方法需要在运行期才能解析出来。那也许就会问,与给定的选择子名称相对应的方法是不是也可以在运行期改变呢?答案是肯定的。若善用此特性,则可发挥巨大优势,因为我们既不需要源代码,也不需要通过通过继承子类来覆写方法就能改变这个类本身的功能。这样一来,新功能将在本类的所有实例中生效,而不是仅限于覆写了相关方法的那些子类实例。此方案经常成为“方法调配”(Method Swizzling)。
(1)在运行期,可以向类中新增或替换选择子所对应的方法实现
(2)使用另一份实现替换原来方法的实现,这叫“方法调配”,此技术可向原有实现中添加新功能
(3)不宜滥用
@implementation UIViewController (Logging)
+ (void)load {
swizzleMethod([self class], @selector(viewDidAppear:), @selector(swizzle_viewDidAppear:));
}
- (void)swizzle_viewDidAppear:(BOOL)animated {
[self swizzle_viewDidAppear:animated]; //看着好像是递归,不过大家记住,这个方法是准备和viewDidAppear这儿方法互换的。所以,在运行期,swizzle_viewDidAppear选择子实际上对应于原有的viewDidAppear方法的实现
NSLog(@"logging: %@", NSStringFromClass([self class]));
}
void swizzleMethod(Class class, SEL originalSelector, SEL swizzleSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzleMethod = class_getInstanceMethod(class, swizzleSelector);
/*
class_addMethod,可以向类中动态的添加方法,用以处理选择子。
尝试添加原selector做一层保护,如果这个类没有实现originalSelector,但其父类实现了,那么class_getInstanceMethod方法返回的是父类的方法。这样的话替换的就是父类的方法,不是我们希望的。所以尝试添加originalSelector,如果已经存在,在用method_exchangeImplementations把原方法的实现和新方法的实现交换。
*/
BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
if (success) {
class_replaceMethod(class, swizzleSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzleMethod);
}
}
参考:http://tech.glowing.com/cn/method-swizzling-aop/
《52个有效方法》