在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有更加灵活的方法吗?在Objective-C编程中,如何实现hook呢?标题有点大,计划分几篇来总结。
本文主要介绍针对selector的hook,主角被标题剧透了———— Method Swizzling 。
Method Swizzling 原理
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。
我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,
我们可以利用 class_replaceMethod 来修改类,
我们可以利用 method_setImplementation 来直接设置某个方法的IMP,……
归根结底,都是偷换了selector的IMP,如下图所示:
Method Swizzling 实践
举个例子好了,我想钩一下NSArray的lastObject 方法,只需两个步骤。
第一步:给NSArray加一个我自己的lastObject
- #import "NSArray+Swizzle.h"
- @implementation NSArray (Swizzle)
- - (id)myLastObject
- {
- id ret = [self myLastObject];
- NSLog(@"********** myLastObject *********** ");
- return ret;
- }
- @end
第二步:调换IMP
- #import <objc/runtime.h>
- #import "NSArray+Swizzle.h"
- int main(int argc, char *argv[])
- {
- @autoreleasepool {
- Method ori_Method = class_getInstanceMethod([NSArray class], @selector(lastObject));
- Method my_Method = class_getInstanceMethod([NSArray class], @selector(myLastObject));
- method_exchangeImplementations(ori_Method, my_Method);
- NSArray *array = @[@"0",@"1",@"2",@"3"];
- NSString *string = [array lastObject];
- NSLog(@"TEST RESULT : %@",string);
- return 0;
- }
- }
- 2013-07-18 16:26:12.585 Hook[1740:c07] ********** myLastObject ***********
- 2013-07-18 16:26:12.589 Hook[1740:c07] TEST RESULT : 3
Method Swizzling 的封装
之前在github上找到的RNSwizzle,推荐给大家,可以搜一下。
- //
- // RNSwizzle.m
- // MethodSwizzle
- #import "RNSwizzle.h"
- #import <objc/runtime.h>
- @implementation NSObject (RNSwizzle)
- + (IMP)swizzleSelector:(SEL)origSelector
- withIMP:(IMP)newIMP {
- Class class = [self class];
- Method origMethod = class_getInstanceMethod(class,
- origSelector);
- IMP origIMP = method_getImplementation(origMethod);
- if(!class_addMethod(self, origSelector, newIMP,
- method_getTypeEncoding(origMethod)))
- {
- method_setImplementation(origMethod, newIMP);
- }
- return origIMP;
- }
- @end
Method Swizzling 危险不危险
针对这个问题,我在stackoverflow上看到了满意的答案,这里翻译一下,总结记录在本文中,以示分享:
使用 Method Swizzling 编程就好比切菜时使用锋利的刀,一些人因为担心切到自己所以害怕锋利的刀具,可是事实上,使用钝刀往往更容易出事,而利刀更为安全。
Method swizzling 可以帮助我们写出更好的,更高效的,易维护的代码。但是如果滥用它,也将会导致难以排查的bug。
背景
好比设计模式,如果我们摸清了一个模式的门道,使用该模式与否我们自己心里有数。单例模式就是一个很好的例子,它饱受争议但是许多人依旧使用它。Method Swizzling也是一样,一旦你真正理解它的优势和弊端,使用它与否你应该就有你自己的观点。
讨论
这里是一些 Method Swizzling的陷阱:
- Method swizzling is not atomic
- Changes behavior of un-owned code
- Possible naming conflicts
- Swizzling changes the method's arguments
- The order of swizzles matters
- Difficult to understand (looks recursive)
- Difficult to debug
我将逐一分析这些点,增进对Method Swizzling的理解的同时,并搞懂如何应对。
http://blog.csdn.net/yiyaaixuexi/article/details/9374411 详情请看念茜的博客