Runtime-Method-Swizzling

假如类中有两个方法
一个是selector1,对应的方法实现是IMP1
一个是selector2,对应的方法实现是IMP2
经过Method-Swizzling操作
可以修改selector对应的实际的方法实现

Method Swizzling发生在运行时,主要是将两个Method交换,Method Swizzling可以写在任何地方,代码执行完毕互换才起作用
每个类都有一个方法列表(Dispatch Table),本质是将类中的SEL和IMP一一对应,Method Swizzling就是对Dispatch Table进行操作,让SEL对应别的IMP
OC的Runtime特性,当调用一个类对象的方法时,内部是给这个类对象发送消息,我们通过这个对象的Dispatch Table ,在这里找出对应的SEL,而SEL对应着IMP,我们通过IMP则找到了方法的实现


在载入方法中做两个方法的实现的交换

#import 

@interface RuntimeObject : NSObject

- (void)test;
- (void)otherTest;
@end
#import "RuntimeObject.h"
#import 
@implementation RuntimeObject


+ (void)load
{
    //获取test方法
    Method test = class_getInstanceMethod(self, @selector(test));
    //获取otherTest方法
    Method otherTest = class_getInstanceMethod(self, @selector(otherTest));
    //交换
    method_exchangeImplementations(test, otherTest);
}
- (void)test{
    NSLog(@"test");
}

- (void)otherTest{
    //因为在load中已经做了两个方法的替换,实际上是调用了test的具体实现
    [self otherTest];
    NSLog(@"otherTest");
}

    RuntimeObject *obj = [[RuntimeObject alloc] init];
    // 调用test方法,只有声明,没有实现
    [obj test];
可以实现所有页面加入统计的实现

当项目完成后,若需要在所有页面加入统计
方法一:手动在每个viewDidLoad的页面添加统计
方法二:在基类添加,所有类都继承基类
方法三:为UIViewController添加Category,在此Category的load方法中执行viewDidLoad和swizzlingViewDidLoad的互换
因为所有的控制器终归是要继承UIViewController,而UIViewController的Category的load互换,在main函数之间就换了
所以,当显示任一UIViewController子类时,即将执行viewDidLoad时,会执行分类的swizzlingViewDidLoad,在此内部结束时再去执行自己本身的viewDidLoad
下面显示类别的实现


#import "UIViewController+TestCategory.h"
#import
@implementation UIViewController (TestCategory)
+ (void)load
{
    [superload];
    // 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。
    Method fromMethod = class_getInstanceMethod([selfclass], @selector(viewDidLoad));
    Method toMethod = class_getInstanceMethod([selfclass], @selector(swizzlingViewDidLoad));
    /**
     *  我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。
     *  而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。
     *  所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。
     */
    if (!class_addMethod([selfclass], @selector(viewDidLoad), method_getImplementation(toMethod), method_getTypeEncoding(toMethod)))
    {
        method_exchangeImplementations(fromMethod, toMethod);
    }
}
- (void)swizzlingViewDidLoad
{
    NSString *str = [NSStringstringWithFormat:@"%@", self.class];
    // 我们在这里加一个判断,将系统的UIViewController的对象剔除掉,只统计自定义页面,不统计系统页面
    if(![str containsString:@"UI"])
    {
        NSLog(@"统计打点 : %@", self.class);
    }
    [selfswizzlingViewDidLoad];
}
@end

你可能感兴趣的:(Runtime-Method-Swizzling)