Method Swizzling && KVC

1,254 阅读3分钟

Method Swizzling

是什么

Method Swizzling的含义是方法交换,其主要作用是在运行时将一个方法的实现替换为另一个方法的实现

交换前: 截屏2021-07-26 下午1.58.37.png 交换后 截屏2021-07-26 下午1.59.09.png

使用场景 eg:通过 Method Swizzling 替换viewController中的生命周期函数,比如ViewDidAppear,打印一些日志,或者做一些路径上传的操作。

注意事项

  • 应该只在+load中执行Method Swizzling。
  • Method Swizzling 应该总是在dispatch_once中执行。保证只交换一次。
  • 使用Method Swizzling 后要记得调用原来方法的实现。
  • 避免命名冲突
  • 如果是继承关系的子类中,交换方法的时候,注意判断交换的方法以及被交换的方法是否都已经实现。 eg:
+ (void)lg_bestMethodSwizzlingWithClass:(Class)cls oriSEL:(SEL)oriSEL swizzledSEL:(SEL)swizzledSEL{

    if (!cls) NSLog(@"传入的交换类不能为空");

    Method oriMethod = class_getInstanceMethod(cls, oriSEL);

    Method swiMethod = class_getInstanceMethod(cls, swizzledSEL);

    if (!oriMethod) { // 避免动作没有意义

        // 在oriMethod为nil时,替换后将swizzledSEL复制一个不做任何事的空实现,代码如下:

        class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));

        method_setImplementation(swiMethod, imp_implementationWithBlock(^(id self, SEL _cmd){

            NSLog(@"来了一个空的 imp");

        }));

    }

    // 一般交换方法: 交换自己有的方法 -- 走下面 因为自己有意味添加方法失败

    // 交换自己没有实现的方法:

    //   首先第一步:会先尝试给自己添加要交换的方法 :personInstanceMethod (SEL) -> swiMethod(IMP)

    //   然后再将父类的IMP给swizzle  personInstanceMethod(imp) -> swizzledSEL

    //oriSEL:personInstanceMethod


    BOOL didAddMethod = class_addMethod(cls, oriSEL, method_getImplementation(swiMethod), method_getTypeEncoding(swiMethod));
 

    if (didAddMethod) {

        class_replaceMethod(cls, swizzledSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));

    }else{

        method_exchangeImplementations(oriMethod, swiMethod);

    }  

}
load VS initialize

加载时刻 load方法时当一个类或者分类被添加到Objective-C运行时调用, 在load_images时调用

加载顺序

  • 类的+load的方法在它的父类+load方法之后调用
  • 分类的+load方法在这个类自己的+load方法之后调用

initialize 运行时以线程安全的方式将初始化消息发送给类。也就是说,initialize由第一个线程向类发送消息,其它的线程向这个类发送消息时会被阻塞,直到initialize完成。 加载顺序 每个类只调用Initialize一次。父类先调用然后调用子类 如果子类没有实现initialize,或者子类显示的调用了[super initialize],那么父类可能会被调用多次。

KVC

KVC是一种键值编码技术,相关的方法有valueforkey以及setValueforKey setValueforKey它的实现逻辑是什么样子的呢 KVC设置值过程

  1. 判断是否存在set<key>或者_set<key>(),setIs<key>
  2. 如果没有1
  • 2.1:判断accessInstanceVariblesDirectly是否返回YES,系统默认YES
  • 2.2:如果是,判断_<key>_is<key><key>is<key>、等实例变量
  • 2.3:直接给这些实例变量赋值
  1. 如果都没有, 则调用setValue: forUndefinedKey报错 KVC取值过程
  2. get<key><key>is<key> or _<key>如果有,则跳转5
  3. 如果1没有,则判断是否为NSArray
  4. 判断是否为NSSet判断
  5. 非集合类型判断
  • 4.1: accessInstanceVariblesDirectly是否返回YES
  • 4.2: _<key>_is<key><key>is<key>这些实例属性是否有值
  • 4.3: 如果找到,直接取值,然后5 5:细节处理
  • 5.1:如果检索到的属性值是对象指针,则只返回结果
  • 5.2: 如果该值是NSNumber支持的标量,存在NSNumber实例中返回
  • 5.3:如果结果是NSNumber不支持的标量类型,则转换为NSValue对象并返回
  1. 都没有则valueForUndefinedKey报错

  2. 集合类型还需要处理