IOS运行时Method Swizzling (2)

前言

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

Method Swizzling 原理

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


IOS运行时Method Swizzling (2)_第1张图片
1.png

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


IOS运行时Method Swizzling (2)_第2张图片
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 
 
@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))