Swizzling方法

1人阅读 评论(0) 收藏 举报

swizzling是一种能够改变已存在的selector的实现的方法,它通过改变selectors在类的映射表中的映射方式修改Objective-C的方法调用。

举个例子,如果我们想追踪各个viewController对用户展示了多少次,我们可以在每个viewController的viewDidAppear:中添加追踪代码,但是那样会产生大量的重复代码块。也可以通过继承的方式来实现,但是这种方式会要求继承UIViewController、UITableViewController、UINavigationController等其他任何的viewController类,这种方式也会产生重复代码。

幸运的是,我们可以在代理中添加swizzling方法来做到,而没有上述的问题:

#import <objc/runtime.h>

@implementation UIViewController (Tracking)

+ (void)load {
    static dispatch_once_t onceToken;
    dospatch_onec(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(xxx_viewWillAppear:);   
        
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        /*
        * 当调整一个类方式的时候使用下面的实现方式:
        * Class class = object_getClass((id)self);
        * ...
        * Method originalMethod = class_getClassMethod(class, originalSelector);
        * Method swizzledMethod = class_getClassMethod(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);
}

现在当任何UIViewController的实例或者子类调用viewWillAppear:都会打印出日志。
使用swizzling方法往视图控制器的生命周期、响应时间、视图绘画或者网络堆栈库里面注入行为具有很长好的效果。当然还有很多适用swizzling技术的场景。

+load vs. +initialize

Swizzling只能在+load方法中实现

在类中,只有两个方法会被运行时自动调用:
* 当类初始加载的时候会调用+load
* 当类或者对象被第一次使用时会调用+initialize
要实现Swizzling方法,+load和+initialize都是可选的,只要实现了方法,就会被执行。但是因为swizzling方式会影响全局的状态,所以尽量减少竟态条件是非常重要的。类在初始化的时候,+load是必然会被执行的,这在改变系统范围的行为的时候提供了一定的一致性。相较之下,+initalize就没有这方面的保证,它有可能永远不会被执行(只有被实例化的时候才会执行)。

dispatch_once

Swizzling应该在dispatch_once中实现

重复强调一下,因为swizzling会改变全局的状态,所以我们需要采取所有的可以采取的预防措施。原子操作就是一种预防措施,它确保了其中的代码只会被执行一次,即使在在不同的线程中。

Selectors,Methods,&Implementations

在Objective-C中,selectors,methods和implementations分别代表了runtime的几个方面,然而在消息发送的过程中,这些概念可以相互转换。
下面是Apple'sObjective-C运行时的官方描述:

Selector(typedef struct objc_selector *SEL):在runtime,Selectors代表方法的名称,selector方法是objective-C在运行时注册(或者映射)的一个C字符串,在类加载的时候,它由编辑器生成,并由runtime自动生成映射。

Method(typedef struct objc_method *Method):在类的定义中代表一个方法的未知类型。

Implementation(typedef id (*IMP)(id, SEL, ...)):这个数据类型是一个指向实现该方法的函数的开始地址,这个函数为当前的CPU架构使用标准的C语言协议。第一个参数是一个指向自身的指针(类的实例地址或者指向元类);第二个参数是selector方法。

理解这些概念的最好方式是:类维维护着一个消息分发列表,表中的入口是方法(Method),方法标记着selector的名称和IMP的指针。

swizzle方法是改变类的分发表中的已存在的selector和implementation的映射关系,使selector与implementation组成新的映射关系。

Invoking _cmd

下面代码可能会导致无限循环:

- (void)xxx_viewWillAppear:(BOOL)animated {
    [self xxx_viewWillAppear:animated];
    NSLog(@"viewWillApear: %@", NSStringFromClass([self class]));
}

然而,并没有。在swizzling的实现过程中,xxx_viewWillAppear:已经被替换成了-viewWillAppear:,如果将[self xxx_viewWillAppear:animated]改成-viewWillAppear:则会发生死循环

记得给swizzled方法添加前缀是一个好的习惯。

总结

很多人认为Swizzling是一种不可靠的技术,容易产生不可预料的行为和后果。但是当做了如下处理后,它将变得非常可靠:
* 总是调用原生方法(除非你有更好的理由不去做):APIs提供了输入输出的协议,但是implementation却没有公开,是一个黑盒子。使用Swizzling方法后,而不是用原生的方法将会导致底层不可预知的崩溃。
* 避免冲突:分类方法使用前缀,确保其它代码库或者依赖库没有实现和你一样的功能。
* 理解原理
* 保持谨慎

Method Swizzling

查看评论

Method Swizzling的方法

开发中总会遇到需要使用Method Swizzling的时候,记录一下Method Swizzling的正确方法一、方法以hookUIViewController 为例#import @impleme...
  • sinat_25544827
  • sinat_25544827
  • 2017-01-17 12:01:55
  • 530

iOS Runtime应用实例(二)method swizzling(方法交叉)

原创Blog,转载请注明出处 http://blog.csdn.net/hello_hwc?viewmode=list 我的stackoverflow本博客关于Runtime的博客链接 iOS...
  • Hello_Hwc
  • Hello_Hwc
  • 2015-11-12 11:10:48
  • 1679

iOS之方法变换(Method Swizzling)

C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序如何运行的。而Objective-C是动态语言,它并非通过调用类的方法来执行功能,而是给对象发送消息,对象在接收到消息之后会...
  • LVXIANGAN
  • LVXIANGAN
  • 2017-07-26 17:21:43
  • 403

method-swizzling 详解 和使用

简介在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实...
  • Erice_e
  • Erice_e
  • 2017-06-15 16:53:24
  • 928

Method Swizzling中的陷阱

这篇文章不是介绍什么是方法交换,这类文章很多,如果你不知道什么是方法交换可以看这篇文章:Method Swizzling...
  • dp948080952
  • dp948080952
  • 2017-02-05 19:55:09
  • 419

Method Swizzling(iOS的hook机制)

为了安全,要为NSUserDefaults加密。但是为NSUserDefaults的每个读写的地方加入加密解密方法也太麻烦。所以想重写NSUserDefaults的读写方法,把加密解密方法内嵌其中,这...
  • u012282115
  • u012282115
  • 2016-01-20 16:04:43
  • 2100

iOS之运行时机制及方法混写method swizzling

runtime method swizzling
  • u011342466
  • u011342466
  • 2016-03-04 15:30:01
  • 1178

IOS之方法混写(swizzling.)

OC中的混写(swizzling)是指透明地把一个方法换成另外一个。简明的说就是在运行时替换方法。利用方法混写可以改变那些没有源代码的对象(包括系统对象)的行为。 方法混写的代码看起来相对比较直观的...
  • chaoyuan899
  • chaoyuan899
  • 2015-03-17 23:00:53
  • 2692

iOS开发 Method Swizzling 方法替换

关于Method Swizzling,有位大神介绍的很详细了,地址:Objective-C的hook方案(一): Method Swizzling 其实呢,菜逼一个的我不怎么懂,就把我项目中用到的贴...
  • syg90178aw
  • syg90178aw
  • 2016-06-28 11:09:23
  • 503

IOS Method Swizzling 替换方法 Objective-C的hook方案

Objective-C的hook方案(一):  Method Swizzling 在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力...
  • u012884714
  • u012884714
  • 2014-04-30 17:44:46
  • 2131
    个人资料
    等级:
    访问量: 420
    积分: 44
    排名: 194万+
    文章分类
    文章存档