介绍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 三个方面接触过:

  1. 通过 Objective-C 源代码 - 接触runtime

    • 在runtime中,通过数据结构来定义的一些类、方法、协议等;

  2. 通过 Foundation 框架的NSObject类定义的方法 - 接触runtime

    • 如isKindOfClass:

    • 如isMemberOfClass:

    • 如respondsToSelector:

    • 如methodForSelector:

  3. 通过对 runtime 函数的直接调用 - 接触runtime

    • objc_ 开头的方法

      1. 如objc_ivar_list

      2. 如objc_property_attribute_t

    • ivar_ 开头的方法

      1. 如ivar_getOffset

      2. 如ivar_list

    • protocol_ 开头的方法

      1. 如protocol_getName

      2. 如protocol_copyPropertyList

    • method_ 开头的方法

      1. 如method_getName

      2. 如method_setImplementation{第二神器用到}

      3. 如method_exchangeImplementations {第二神器用到}

      4. 如method_getTypeEncoding{第二神器用到}

    • sel_ 开头的方法

      1. 如sel_getName

      2. 如sel_registerName

    • imp_ 开头的方法

      1. 如imp_getBlock

      2. 如imp_removeBlock

      3. 如imp_implementationWithBlock

    • class_ 开头的方法

      1. 如class_addIvar

      2. 如class_getName

      3. 如class_addProperty

      4. 如class_getInstanceMethod {第二神器用到}write by erliucxy

      5. 如class_replaceMethod {第二神器用到}

      6. 如class_addMethod {第二神器用到}

第三部分:runtime的第二神器-Method Swizzling

3.1本质就是

  1. 交换IMP和SEL的对应关系,以达到替换方法实现的目的;

  2. 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常见的问题

  1. 多继承关系的类使用Method Swizzling时,应该注意的方面是?

    • 多个有继承关系的类的对象Swizzle时,先从父对象开始。 这样才能保证子类方法拿到父类中的被Swizzle的实现。

    • 在load中Swizzle不用担心这种问题,因为load类方法会默认从父类开始调用。

    • 深入了解//TODO

  2. 使用Method Swizzling时解决命名冲突

  3. 为什么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就失效了。

  4. 为什么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?