Objective-C中的Method Swizzling
来源:互联网 发布:kontakt mac 音源入库 编辑:程序博客网 时间:2024/06/05 03:26
Objective-C中的Method Swizzling
了解Method Swizzling前应该先来了解一下Runtime与方法相关的一些内容。
1.基础数据类型
SEL
SEL,选择器,是表示一个方法的selector的指针,定义如下:
typedef struct objc_selector *SEL;
方法的selector用于表示运行时方法的名字。Objective-C在编译时,会根据每一个方法的名字,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL。
SEL reloadSel = @selector(reloadData);
SEL的本质是一个指向方法的指针(是根据方法名hash化了的KEY值,能唯一代表一个方法)。两个方法只要方法名相同,那么方法的SEL就是一样的。每一个方法都对应着一个SEL。同一个类(及类的继承体系)中,不能存在2个同名的方法,即使参数类型不同也不行。不同的类可以拥有相同的selector,不同类的实例对象执行相同的selector时,会在各自的方法列表中去根据selector去寻找自己对应的IMP。
IMP
IMP是一个函数指针,指向方法实现的首地址。定义如下:
id (*IMP)(id, SEL, ...)
第一个参数是指向self的指针(如果是实例方法,则是类实例的内存地址;如果是类方法,则是指向元类的指针),第二个参数是方法选择器(selector),后面是方法的实际参数列表。
SEL就是为了查找方法的最终实现IMP的。由于每个方法对应唯一的SEL,因此我们可以通过SEL方便快速准确地获得它所对应的 IMP。取得IMP后,就获得了执行这个方法代码的入口点,然后就可以像调用普通的C语言函数一样来使用这个函数指针了。
通过取得IMP,我们可以跳过Runtime的消息传递机制,直接执行IMP指向的函数实现,这样省去了Runtime消息传递过程中所做的一系列查找操作,会比直接向对象发送消息高效一些。
Method
Method用于表示类定义中的方法,定义如下:
typedef struct objc_method *Method;struct objc_method { SEL method_name OBJC2_UNAVAILABLE; // 方法名 char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; // 方法实现}
这个结构体中包含一个SEL和IMP,相当于在SEL和IMP之间作了一个映射。有了SEL,就可以找到对应的IMP,从而调用方法的实现代码。
2.方法相关操作函数
Runtime提供了一系列的方法来处理与方法相关的操作。包括方法本身及SEL。
// 调用指定方法的实现id method_invoke ( id receiver, Method m, ... );// 调用返回一个数据结构的方法的实现void method_invoke_stret ( id receiver, Method m, ... );// 获取方法名SEL method_getName ( Method m );// 返回方法的实现IMP method_getImplementation ( Method m );// 获取描述方法参数和返回值类型的字符串const char * method_getTypeEncoding ( Method m );// 获取方法的返回值类型的字符串char * method_copyReturnType ( Method m );// 获取方法的指定位置参数的类型字符串char * method_copyArgumentType ( Method m, unsigned int index );// 通过引用返回方法的返回值类型字符串void method_getReturnType ( Method m, char *dst, size_t dst_len );// 返回方法的参数的个数unsigned int method_getNumberOfArguments ( Method m );// 通过引用返回方法指定位置参数的类型字符串void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len );// 返回指定方法的方法描述结构体struct objc_method_description * method_getDescription ( Method m );// 设置方法的实现IMP method_setImplementation ( Method m, IMP imp );// 交换两个方法的实现void method_exchangeImplementations ( Method m1, Method m2 );
接下来真正来看Method Swizzling的内容。
3.Method Swizzling
Method Swizzling是改变一个selector的实际实现的技术。通过它,我们可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现。
当我们需要在整个工程中修改系统某个类某个方法的实现时,一种方法是创建一个子类重写这个方法,然后其他类都继承这个类,不过这种方法在工程开始之初还可以,但是如果是在一个已有工程的基础上新加需求,如果修改全部的类都继承这个类,改动量就比较大了。这时候就可以用Method Swizzling来实现。
举个例子:
@implementation UIViewController (LoadView)+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; // When swizzling a class method, use the following: // Class class = object_getClass((id)self); SEL originalSelector = @selector(viewDidLoad:); SEL swizzledSelector = @selector(s_viewDidLoad:); Method original_ViewDidLoad = class_getInstanceMethod(class, originalSelector); Method swizzled_ViewDidLoad = class_getInstanceMethod(class, swizzledSelector); method_exchangeImplementations(original_ViewDidLoad, swizzled_ViewDidLoad); });}- (void)s_viewDidLoad { [self initLoadView]; [self s_viewDidLoad];}- (void)initLoadView { // add load view}@end
在上面的例子中,我们通过method swizzling修改了UIViewController的@selector(viewDidLoad:)对应的函数指针,让它的实现指向了我们自定义的s_viewDidLoad的实现。这样,当UIViewController及其子类的对象调用viewDidLoad时,都会执行initLoadView来添加load view。
通过上面的例子,可以总结出几个method swizzling的用法和注意事项:
1. method swizzling要在+load中执行
由于method swizzling会影响到类的全局状态,而+load能保证在类的初始化过程中被加载,且能够保证这种改变应用级别的行为的一致性,所以要在+load中执行。
2. method swizzling要在dispatch_once中执行
同样由于method swizzling会影响到类的全局状态,所以需要在运行时采取一些预防措施。原子性就是这样一种措施,可以确保代码只被执行一次,不管有多少个线程。GCD的dispatch_once完全可以确保这种行为。
3. 为了避免冲突,要给自定义的方法加前缀,从而可以避免和所依赖的代码库出现命名冲突。
4. 无限循环,上面的例子中,在s_viewDidLoad中调用了[self s_viewDidLoad:animated],咋看上去会导致死循环。但是,在swizzling的过程中,s_viewDidLoad已经被重定向UIViewController类的viewDidLoad:中,不会产生无限循环。但是如果我们调用的是[self viewDidLoad:animated],则会产生无限循环,因为这个方法的实现在运行时已经被重新指定为s_viewDidLoad了。
- Objective-C中的Method Swizzling
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- Objective-C Method Swizzling
- [iOS]Objective-C Method Swizzling
- Objective-C的hook方案: Method Swizzling
- Objective-C的hook方案: Method Swizzling
- Objective-C Method Swizzling 的最佳实践
- Objective-C Method Swizzling 的最佳实践
- Objective-c Method Swizzling的详细使用方法
- Objective-C Method Swizzling 的最佳实践
- Objective-C Runtime 运行时:Method Swizzling
- iOS安全–Objective-C Method Swizzling
- Objective-C Runtime :Method Swizzling - method_exchangeImplementations
- iOS安全–Objective-C Method Swizzling
- Objective-C Method Swizzling 的最佳实践
- Builder模式
- 怎样使android的view动画循环弹动
- 获得与设置系统默认设备主音量音量大小以及静音控制函数
- project euler 97
- project euler 99
- Objective-C中的Method Swizzling
- [Java开发之路](5)异常详解
- Core Animation基础
- (转)Android如何编程设置APP安装位置(外部存储或内部存储)?
- MQTT协议笔记之连接和心跳
- 欢迎使用CSDN-markdown编辑器
- Android5.0 Battery开发(一)
- (转)Android属性设置android:noHistory="true"
- 今天公司用的mms 域控打不开