Objective-C之Method Swizzle

一、原理
我们平时编写的OC代码,底层都是基于Runtime来实现的。Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一。
1). 在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有更加灵活的方法 Method Swizzle。
2). Method Swizzle 指的是改变一个已存在的选择器对应的实现的过程。OC中方法的调用能够在运行时通过改变,通过改变类的调度表中选择器到最终函数间的映射关系。
3). 在OC中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用OC的动态特性,可以实现在运行时偷换selector对应的方法实现。
4). 每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的方法实现。
5). 我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP。
6). 我们可以利用 class_replaceMethod 来修改类。
7). 我们可以利用 method_setImplementation 来直接设置某个方法的IMP。
8). 归根结底,都是偷换了selector的IMP。。

二、实现

import

@implementation UIViewController (Tracking)

  • (void)load
    {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    Class class = [self class];
    SEL originalSelector = @selector(viewWillAppear:);
    SEL swizzledSelector = @selector(clq_viewWillAppear:);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);  
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        // 如果是替换类方法,使用下面
        // Class class = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(class, orginalSelector);
        // Method swizzledMethod = class_getClassMethod(class, swizzledSelctor);
    
        BOOL didAddMethod = 
                class_addMethod(class,
                           originalSelector,
                            method_getImplementation(swizzledMethod),
                            method_getTypeEncoding(swizzledMethod));
    
         if (didAddMethod) {
             class_replaceMethod(class,
                    swizzledSelector,
                    method_getImplementation(originalMethod),
                    method_getTypeEncoding(originalMethod));       
         } else {
               method_exchangeImplementations(orgiginalMethod, swizzledMethod);
         }
    

    });
    }

pragma mark - Method Swizzling

  • (void)clq_viewWillAppear:(BOOL)animated
    {
    [self clq_viewWillAppear:animated];
    }

三、注意点
1、object_getclass((id)self)和[self class]的区别
1)runtime经典图解分析


Paste_Image.png

2)源码
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}

 + (Class)class {
      return self;
  }

  - (Class)class {
     return object_getClass(self);
  }

 3)[self class] 指向类对象,类对象存储成员函数,而object_getclass((id)self)指向meta类,meta类指向类方法

2、clq_viewWillAppear实现是否造成递归
答案是否的,clq_viewWillAppear方法里面调用clq_viewWillApear方法的时候,由于clq_viewWillApear和viewWillApear的实现已交换了,这时候调用clq_viewWillApear的时候,实际上是调用swizzle之前的方法,viewWillApear,所以不会造成递归调用的

3、不用class_addMethod进行判断,简单用method_exchangeImplementations有什么问题
由于当要重写的方法(overridden)并没有在目标类中实现(notimplemented),而是在其父类中实现了。这时候用method_exchangeImplementations是达不到目的的,需要在目标类增加一个新的实现方法(override)

你可能感兴趣的:(Objective-C之Method Swizzle)