Method Swizzle讲解
首先上图,图中是我们的类的结构关系图
有一个People基类,他有一个与生俱来的的本能:eat,另外有一个Kid,这个Kid继承了这个本能,并且他学会了新的技能:say,我们想在Kid eat的时候先通知willEat,然后在eat;在say的时候,先通知willSay,然后在say。下面我们来上代码。
1 Class class = [self class]; 2 3 SEL originalSelector = @selector(eat:); 4 SEL swizzledSelector = @selector(willEat:); 5 6 // SEL originalSelector = @selector(say:); 7 // SEL swizzledSelector = @selector(willSay:); 8 9 Method originalMethod = class_getInstanceMethod(class, originalSelector); 10 Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 11 12 BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); 13 if (didAddMethod) { 14 // 添加成功,说明该类的originalSelector方法并不存在,可能是继承父类People而来的 15 NSLog(@"%@方法不存在,添加成功", NSStringFromSelector(originalSelector)); 16 class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); 17 NSLog(@"将%@的方法IMP替换成People的eat", NSStringFromSelector(swizzledSelector)); 18 } else { 19 // 添加失败,说明该类的originalSelector方法已经存在 20 NSLog(@"%@方法已存在,添加失败", NSStringFromSelector(originalSelector)); 21 method_exchangeImplementations(originalMethod, swizzledMethod); 22 NSLog(@"方法交换成功"); 23 }
以上代码完成了我们的目标
1、首先我们先分析eat,eat是从People继承而来,所以,在Kid的method_list中找不到这个方法,这个时候调用 class_addMethod ,将会返回YES,并且我们看到了添加这个eat方法的时候,对应的IMP是willEat的IMP
即:eat —> willEat IMP, willEat —> willEat IMP
这个时候代码会执行 class_replaceMethod 操作,它的作用就是将SEL的IMP替换成指定的IMP,从代码中我们可以看出,我们将willEat的IMP替换成了People的eat的IMP
即:eat —> willEat IMP, willEat —> People eat IMP
- (void)willEat:(NSString *)food { NSLog(@"Kid will eat: %@", food); [self willEat:food]; }
这样一来,当调用[kid eat:@"orange"],输出如下:
1 Kid will eat: orange 2 People eat: orange
2、然后我们再来分析一下say,say是Kid自身的方法,所以在method_list中可以查到,这个时候会执行 method_exchangeImplementations ,它的作用是将两个SEL的IMP交换
交换前:say —> say IMP ,willSay —> willSay IMP
交换后:say —> willSay IMP ,willSay —> say IMP
这样一来,当调用[kid say: @"hello!"],输出如下:
1 Kid will say: hello! 2 Kid say: hello!
以上就是对Method Swizzle的分析,注意:class_addMethod:目的是解决当前交换的原始方法,是继承自父类的方法,而自身的method_list中不存在,此时如果调用method_exchangeImplementations交换方法,将会失败。