介绍Runtime
write by coderiding,erliucxy 中山 东区 广东 2018-02-23- 17:19:20
开始之前,你可以直接查看图片文章图片链接
第一部分:runtime的第一神器-Associated Objects
Associated Objects的介绍:中文翻译为关联对象、对象关联、关联引用
文字总结write by erliucxy
可以实现什么:给系统的类增加属性对象。
方便的地方是://TODO
1.1主要涉及3个方法
objc_setAssociatedObject 设置
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);objc_getAssociatedObject 获取
OBJC_EXPORT id objc_getAssociatedObject ( id object , const void *key )
OBJC_AVAILABLE ( 10.6 , 3.1 , 9.0 , 1.0 );objc_removeAssociatedObjects 移除
不应该自己手动调用这个函数,规范的方法是:调用 objc_setAssociatedObject 方法并传入一个 nil 值来清除一个关联;因为可能会导致其他客户对其添加的属性也被移除了
基本参数说明
1.2关联策略objc_AssociationPolicy
关联对象的行为,它可以指定Objc内存管理的引用计数机制。
以 OBJC_ASSOCIATION_ASSIGN 类型关联在对象上的弱引用不代表0 retian的 weak 弱引用,行为上更像 unsafe_unretained 属性,所以当在你的视线中调用weak的关联对象时要相当小心。write by erliucxy
1.3 Associated Objects常见的使用例子
001--使用例子
相当于加了一个 @property (nonatomic, strong)
002--使用例子
相当于加了一个 @property (assign) 或 @property (unsafe_unretained)
003—使用例子
相当于加了一个 @property (nonatomic, strong)
004 使用例子
相当于加了一个 @property (atomic, strong)
005 使用例子
相当于加了一个 @property (nonatomic, copy)
006 使用例子
相当于加了一个 @property (nonatomic, strong)
007 使用例子
// 相当于加了一个 @property (nonatomic, strong)
008合体例子
Associated Objects合体使用的例子
009 合体例子
010 合体例子write by erliucxy
第二部分:你在什么地方接触过runtime?
2.1 三个方面接触过:
通过 Objective-C 源代码 - 接触runtime
在runtime中,通过数据结构来定义的一些类、方法、协议等;
通过 Foundation 框架的NSObject类定义的方法 - 接触runtime
如isKindOfClass:
如isMemberOfClass:
如respondsToSelector:
如methodForSelector:
通过对 runtime 函数的直接调用 - 接触runtime
objc_ 开头的方法
如objc_ivar_list
如objc_property_attribute_t
ivar_ 开头的方法
如ivar_getOffset
如ivar_list
protocol_ 开头的方法
如protocol_getName
如protocol_copyPropertyList
method_ 开头的方法
如method_getName
如method_setImplementation{第二神器用到}
如method_exchangeImplementations {第二神器用到}
如method_getTypeEncoding{第二神器用到}
sel_ 开头的方法
如sel_getName
如sel_registerName
imp_ 开头的方法
如imp_getBlock
如imp_removeBlock
如imp_implementationWithBlock
class_ 开头的方法
如class_addIvar
如class_getName
如class_addProperty
如class_getInstanceMethod {第二神器用到}write by erliucxy
如class_replaceMethod {第二神器用到}
如class_addMethod {第二神器用到}
第三部分:runtime的第二神器-Method Swizzling
3.1本质就是
交换IMP和SEL的对应关系,以达到替换方法实现的目的;
Method Swizzling就是OC中的Hook
3.2必须明确的概念
IMP本质:是一个函数指针,保存了方法地址,指向方法的实现,这是由编译器生成的。表示同一个id + 同一个SEL = 同一个IMP
id的本质:id其实就是一个指向objc_object结构体指针,它包含一个Class isa成员;根据isa指针就可以顺藤摸瓜找到对象所属的类write by erliucxy
SEL的本质:SEL 是系统在编译过程中,根据 方法的名字 以及 参数序列 生成一个用来区分这个方法的唯一 ID 编号;这个 ID 就是 SEL 类型的。和C的函数指针还不一样,函数指针直接保存了方法的地址,但是SEL只是方法编号。
Method的本质:指向objc_method结构体指针;是一种代表类中的某个方法的类型。
method_name:方法名类型为SEL,前面提到过相同名字的方法即使在不同类中定义,它们的方法选择器也相同。
method_types:方法类型method_types是个char指针,其实存储着方法的参数类型和返回值类型。
method_imp:指向了方法的实现,本质上是一个函数指针,后面会详细讲到
// 疑问:什么时候调用class_addMethod?调用它有什么作用?
// 作用:官方解答:Adds a new method to a class with a given name and implementation.class_addMethod will add an override of a superclass's implementation
// 作用:中文翻译:通过给的name和imp添加一个新的方法到类中。它将添加一个父类已经实现的imp。
// 注意:调用这个方法不能直接改变该类中已经存在的imp,如果要改变,使用method_setImplementation
// 当需要替换的方法可能有不存在的情况时,可以考虑使用该方法。write by erliucxy
cls:The class you want to modify.
name:A selector that identifies the method whose implementation you want to replace.
imp:The new implementation for the method identified by name for the class identified by cls.
types:An array of characters that describe the types of the arguments to the method. For possible values, see Objective-C Runtime Programming Guide > Type Encodings. Since the function must take at least two arguments—self and _cmd, the second and third characters must be “@:” (the first character is the return type).
// 疑问:什么时候调用class_replaceMethod?调用它有什么作用?
// 作用:官方:Replaces the implementation of a method for a given class.
// 作用:解释:为给定的类替换imp
// 当仅仅需要为一个方法设置其实现方式时使用
// OC实现的编码类型
3.3 用一个图来表示Method Swizzling在做什么?
交换IMP和SEL的对应关系,以达到替换方法实现的目的;
3.4 Method Swizzling的实现方案
Method Swizzling的实现方案一:
Method Swizzling的实现方案二:--最佳方案
如果类中没有实现 Original selector 对应的方法,那就先添加 Method,并将其 IMP 映射为 Swizzle 的实现。然后替换 Swizzle selector 的 IMP 为 Original 的实现;否则交换二者 IMP。
有时为了避免方法命名冲突和参数 _cmd 被篡改,也会使用下面这种『静态方法版本』的 Method Swizzle。CaptainHook 中的宏定义也是采用这种方式,比较推荐:
3.5 Method Swizzling常见的使用例子
001 使用例子
002 使用例子--RNSwizzle
003 使用例子—猿题库
004 使用例子
005 使用例子
上面的代码简化
这是因为class_replaceMethod方法其实能够覆盖到class_addMethod和method_setImplementation两种场景, 对于第一个class_replaceMethod来说, 如果viewWillAppear:实现在父类, 则执行class_addMethod, 否则就执行method_setImplementation将原方法的IMP指定新的代码块; 而第二个class_replaceMethod完成的工作便只是将新方法的IMP指向原来的代码.
但此处需要特别注意交换的顺序,应该优先把新的方法指定原IMP,再修改原有的方法的IMP.write by erliucxy
为什么呢?
3.6 Method Swizzling常见的问题
多继承关系的类使用Method Swizzling时,应该注意的方面是?
多个有继承关系的类的对象Swizzle时,先从父对象开始。 这样才能保证子类方法拿到父类中的被Swizzle的实现。
在load中Swizzle不用担心这种问题,因为load类方法会默认从父类开始调用。
深入了解//TODO
使用Method Swizzling时解决命名冲突
为什么Method Swizzling应该在+load方法中实现?write by erliucxy
在 Objective-C 的运行时中,每个类有两个方法都会自动调用。+load 是在一个类被初始装载时调用,+initialize 是在应用第一次调用该类的类方法或实例方法前调用的。两个方法都是可选的,并且只有在方法被实现的情况下才会被调用。
+load在每个类被装载到Runtime的时候调用
+initialize 在每个类第一次被发送消息的时候调用。
之所以要在+load中进行,是因为方法交叉影响的是全局状态,+load中能保证在class 装载的时候进行交叉,而initialize没办法做到。
Swizzling在+load中执行时,不要调用[super load];因为如果是多继承,并且对同一个方法都进行了Swizzling,那么调用[super load]以后,父类的Swizzling就失效了。
为什么Method Swizzling应该在dispatch_once中完成?
由于 swizzling 改变了全局的状态,所以我们需要确保每个预防措施在运行时都是可用的。原子操作就是这样一个用于确保代码只会被执行一次的预防措施,就算是在不同的线程中也能确保代码只执行一次。Grand Central Dispatch 的 dispatch_once 满足了所需要的需求,并且应该被当做使用 swizzling 的初始化单例方法的标准。
dispatch_once保证方法交叉只交叉一次
write by coderiding,erliucxy 中山 东区 广东 2018-02-23- 17:19:20
第四部分:疑问
4.1 Method Swizzling危险吗?
What are the Dangers of Method Swizzling in Objective C?
分析 Here are some of the pitfalls of 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
Last updated
Was this helpful?