美文网首页
IOS运行时Method Swizzling (2)

IOS运行时Method Swizzling (2)

作者: SHUTUP | 来源:发表于2016-03-02 22:00 被阅读118次

    前言

    上一篇简单介绍了一下通过运行时添加方法。这篇文章呢,侧重于方法的hook。

    Method Swizzling 原理

    在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现。每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。


    1.png

    利用运行时的一些方法,我们可以做到对系统原有方法的hook,也叫method swizzling。


    2.png

    实例举例

    最近在项目中接入了友盟的数据统计,其中有一个功能,可以统计app各个页面的打开次数,时间等。

    实现页面的统计需要在每个View中配对调用如下方法:
    - (void)viewWillAppear:(BOOL)animated { 
    [super viewWillAppear:animated]; 
    [MobClick beginLogPageView:@"PageOne"];//("PageOne"为页面名称,可自定义) 
    }
    - (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 
    [MobClick endLogPageView:@"PageOne"];
     }
    

    就像文档描述的,我们需要在生命周期函数中添加语句,但由于目前项目已经基本稳定,因此一个页面一个页面的改,难免费时费力。于是考虑AOP,直接hook掉这些函数。

    //demo
    #import <objc/runtime.h>
     
    @implementation UIViewController (Tracking)
     
    + (void)load {
            static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Class class = [self class];         
            // When swizzling a class method, use the following:
           // Class class = object_getClass((id)self);
     
            SEL originalSelector = @selector(viewWillAppear:);
                        SEL swizzledSelector = @selector(xxx_viewWillAppear:);
     
            Method originalMethod = class_getInstanceMethod(class, originalSelector);
                        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
     
            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(originalMethod, swizzledMethod);
            }
        });
    }
     
    #pragma mark - Method Swizzling
     
    - (void)xxx_viewWillAppear:(BOOL)animated {
            [self xxx_viewWillAppear:animated];
        NSLog(@"viewWillAppear: %@", self);
    }
     
    @end
    

    选择器、方法与实现

    在Objective-C中,选择器(selector)、方法(method)和实现(implementation)是运行时中一个特殊点,虽然在一般情况下,这些术语更多的是用在消息发送的过程描述中。

    以下是Objective-C Runtime Reference中的对这几个术语一些描述:

    Selector(typedef struct objc_selector *SEL):用于在运行时中表示一个方法的名称。一个方法选择器是一个C字符串,它是在Objective-C运行时被注册的。选择器由编译器生成,并且在类被加载时由运行时自动做映射操作。
    Method(typedef struct objc_method Method):在类定义中表示方法的类型
    Implementation(typedef id (
    IMP)(id, SEL, …)):这是一个指针类型,指向方法实现函数的开始位置。这个函数使用为当前CPU架构实现的标准C调用规范。第一个参数是指向对象自身的指针(self),第二个参数是方法选择器。然后是方法的实际参数。
    理解这几个术语之间的关系最好的方式是:一个类维护一个运行时可接收的消息分发表;分发表中的每个入口是一个方法(Method),其中key是一个特定名称,即选择器(SEL),其对应一个实现(IMP),即指向底层C函数的指针。

    为了swizzle一个方法,我们可以在分发表中将一个方法的现有的选择器映射到不同的实现,而将该选择器对应的原始实现关联到一个新的选择器中。

    参考

    Objective-C Runtime 运行时之四:Method Swizzling
    iOS黑魔法-Method Swizzling

    相关文章

      网友评论

          本文标题:IOS运行时Method Swizzling (2)

          本文链接:https://www.haomeiwen.com/subject/ehbnkttx.html