Objective-C的hook方案(一): Method Swizzling
2016-01-19 17:06
603 查看
Objective-C的hook方案(一): Method Swizzling
在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有更加灵活的方法吗?在Objective-C编程中,如何实现hook呢?标题有点大,计划分几篇来总结。
本文主要介绍针对selector的hook,主角被标题剧透了———— Method Swizzling 。
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。
我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP,
我们可以利用 class_replaceMethod 来修改类,
我们可以利用 method_setImplementation 来直接设置某个方法的IMP,
……
归根结底,都是偷换了selector的IMP,如下图所示:
举个例子好了,我想钩一下NSArray的lastObject 方法,只需两个步骤。
第一步:给NSArray加一个我自己的lastObject
#import "NSArray+Swizzle.h"
@implementation NSArray (Swizzle)
- (id)myLastObject
{
id ret = [self myLastObject];
NSLog(@"********** myLastObject *********** ");
return ret;
}
@end
乍一看,这不递归了么?别忘记这是我们准备调换IMP的selector,[self myLastObject] 将会执行真的 [self lastObject] 。
第二步:调换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;
}
}
控制台输出Log:
2013-07-18 16:26:12.585 Hook[1740:c07] ********** myLastObject ***********
2013-07-18 16:26:12.589 Hook[1740:c07] TEST RESULT : 3
结果很让人欣喜,是不是忍不住想给UIWebView的loadRequest: 加 TODO 了呢?
之前在github上找到的RNSwizzle,推荐给大家,可以搜一下。
[cpp] view
plain copy
print?
//
// 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
详情请看念茜的博客
在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有更加灵活的方法吗?在Objective-C编程中,如何实现hook呢?标题有点大,计划分几篇来总结。
本文主要介绍针对selector的hook,主角被标题剧透了———— Method Swizzling 。
Method Swizzling 原理
在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。每个类都有一个方法列表,存放着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的selector,[self myLastObject] 将会执行真的 [self lastObject] 。
第二步:调换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;
}
}
控制台输出Log:
2013-07-18 16:26:12.585 Hook[1740:c07] ********** myLastObject ***********
2013-07-18 16:26:12.589 Hook[1740:c07] TEST RESULT : 3
结果很让人欣喜,是不是忍不住想给UIWebView的loadRequest: 加 TODO 了呢?
Method Swizzling 的封装
之前在github上找到的RNSwizzle,推荐给大家,可以搜一下。[cpp] view
plain copy
print?
//
// 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
详情请看念茜的博客
相关文章推荐
- 所有类的超类:Object类
- oc AssociatedObject (oc)
- jQuery.isEmptyObject() 函数详解
- 【转】IOS基础:深入理解Objective-c中@class的含义
- org.json.JSONException: Value of type java.lang.String cannot be converted to JSONObject
- object-c编程tips-jastor自动解析
- 使用CocoaPods开发并打包静态库
- GitHub 上排名前 100 的 Objective-C 项目简介
- 将 Objective-C 代码迁移到 Swift
- Java ObjectMapper String转HashMap
- #Android##bug#ObjectInputStream eof
- NSMutableArray的removeAllObjects崩溃问题
- Objective-C内存管理
- OC笔记一:Objective-C简介
- Object-C的浅拷贝与深拷贝区别
- Objectness 理解,整理及总结
- IOS开发——Swift和Objective-C交互时的一些注意点
- object超类的"三个子类"
- 使用系统的AVMetadataObject类实现二维码扫描
- JSONObject使用方法