一."Black Magic":Method Swizzling 利用 Runtime 特性把一个方法的实现与另一个方法的实现进行替换,也可以用runtime的四维理解——修改Dispatch Table让一个方法的IMP对应到我们指定的IMP上去


二.实例说明:比如我们想要在APP中记录每一个ViewController的出现次数


三.实例分析

  1. 第一种思路就是在ViewController出现的一瞬间(viewDidAppear)我就用记录工具记录一条日志。

     @implementation MyViewController ()
    
     - (void)viewDidAppear:(BOOL)animated
    {
    [super viewDidAppear:animated]; // Custom code // Logging
    [Logging logWithEventName:@“my view did appear”];
    } - (void)myButtonClicked:(id)sender
    {
    // Custom code // Logging
    [Logging logWithEventName:@“my button clicked”];
    }

    暴力方法

    *缺点就在于,我要记录所有的VC那么我就要在所有VC中去手写这些代码?那聪明的你一定会想到Catagory。但是仔细想一想你需要继承UIViewController、UITableViewController、UICollectionViewController所有这些 VC添加类别,而且违背了苹果设计Catagory的目的(为了想类添加一些代码而不是替换)

  2. 第二种思路就是我们今天的主角method swizzling

三.Demo

 #import <objc/runtime.h>

 @implementation UIViewController (Tracking)

 + (void)load {
//保证只替换一次
static dispatch_once_t onceToken;
dispatch_once(&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); 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 {
//这里需要解释一下,因为我们在ViewwillAppear中需要去call父类的实现。这个这行代码调用的时候xxx_viewWillAppear与viewWillAppear方法已经替换。所以不是表面看上去的递归形式
[self xxx_viewWillAppear:animated]; [Logging logWithEventName:NSStringFromClass([self class])];
} @end

为UIViewController添加分类

  解释1.这里唯一可能需要解释的是 class_addMethod 。要先尝试添加原 selector 是为了做一层保护,因为如果这个类没有实现 originalSelector ,但其父类实现了,那 class_getInstanceMethod 会返回父类的方法。这样 method_exchangeImplementations 替换的是父类的那个方法,这当然不是你想要的。所以我们先尝试添加 orginalSelector ,如果已经存在,再用 method_exchangeImplementations 把原方法的实现跟新的方法实现给交换掉

  解释2:Swizzling总是在+load中执行,Because method swizzling affects global state, it is important to minimize the possibility of race conditions. +load is guaranteed to be loaded during class initialization, which provides a modicum of consistency for changing system-wide behavior. By contrast, +initialize provides no such guarantee of when it will be executed—in fact, it may never be called, if that class is never messaged directly by the app.da大概意思是因为方+load是保证是在类初始化加载,从而改变系统行为提供了些许的一致性。相比之下,没有提供这样的保证+init(),如果APP没有直接调用,可能永远不会执行。

  

 void (gOriginalViewDidAppear)(id, SEL, BOOL);

 void newViewDidAppear(UIViewController *self, SEL _cmd, BOOL animated)
{
// call original implementation
gOriginalViewDidAppear(self, _cmd, animated); // Logging
[Logging logWithEventName:NSStringFromClass([self class])];
} + (void)load
{
Method originalMethod = class_getInstanceMethod(self, @selector(viewDidAppear:));
gOriginalViewDidAppear = (void *)method_getImplementation(originalMethod); if(!class_addMethod(self, @selector(viewDidAppear:), (IMP) newViewDidAppear, method_getTypeEncoding(originalMethod))) {
method_setImplementation(originalMethod, (IMP) newViewDidAppear);
}
}

直接取代老方法而不是替换


四.总结What are the Dangers of Method Swizzling in Objective C?

  • Method swizzling is not atomic
  • Changes behavior of un-owned code
  • Possible naming conflicts
  • Swizzling changes the method's arguments
  • The order of swizzles matters
  • Difficult to understand (looks recursive)
  • Difficult to debug

iOS开发那些事儿(四)the dark arts of the Objective-C runtime的更多相关文章

  1. iOS开发RunLoop学习:四:RunLoop的应用和RunLoop的面试题

    一:RunLoop的应用 #import "ViewController.h" @interface ViewController () /** 注释 */ @property ( ...

  2. iOS开发——技术精华Swift篇&Swift 2.0和Objective-C2.0混编之第三方框架的使用

    swift 语言是苹果公司在2014年的WWDC大会上发布的全新的编程语言.Swift语言继承了C语言以及Objective-C的特性,且克服了C语言的兼容性问题.Swift语言采用安全编程模式,且引 ...

  3. IOS开发之路四(UITabBarController)

    前两天看了看斯坦福大学的iphone开发公开课,讲的倒是不错,可看的我云里雾里的,不怎么讲基础和原理,不太适合初学者.今天看了一上午ios5基础教程这本书感觉有点头绪了....废话少说,讲一讲我上午做 ...

  4. iOS开发那些事儿(二)热补丁

    一.热补丁作用:修复导致崩溃的错误.替换/增加方法.替换原来的界面等等 二.实现手段:JSPatch (使用Objective-C Objective-C和JavaScript jspatch桥.你可 ...

  5. iOS开发那些事儿(六)Git分之策略

    git 分支策略 将要介绍的这个模型不会比任何一套流程内容多,每个团队成员都必须遵守,这样便于管理软件开发过程. 既分散又集中 我们使用的,且与这个分支模型配合的非常好的库,他有一个“真正”的中央仓库 ...

  6. iOS开发那些事儿(五)Objective-C浅拷贝与深拷贝

    浅拷贝:copy操作出来的对象指针直接指向模板的地址.即两个对象公用一块内存地址 #import <Foundation/Foundation.h> int main(int argc, ...

  7. iOS开发-OC语言 (四)数组

    知识点 1.NSArray 2.NSMutableArray 1.数组的基本用法: 2.数组的遍历 3.数组排序 ===========   NSArray  不可变数组  ============= ...

  8. 【Swift 3.1】iOS开发笔记(四)

    一.唱片旋转效果(360°无限顺时针旋转) func animationRotateCover() { coverImageView.layer.removeAllAnimations() let a ...

  9. 从零开始学ios开发(十四):Navigation Controllers and Table Views(上)

    这一篇我们将学习一个新的控件Navigation Controller,很多时候Navigation Controller是和Table View紧密结合在一起的,因此在学习Navigation Co ...

随机推荐

  1. 【进制问题】【HDU2056】A + B Again

    A + B Again Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  2. Oracle Golden Gate - 概念和机制 (ogg)

    Golden Gate(简称OGG)提供异构环境下交易数据的实时捕捉.变换.投递. OGG支持的异构环境有: OGG的特性: 对生产系统影响小:实时读取交易日志,以低资源占用实现大交易量数据实时复制 ...

  3. <audio>使用2

    1.属性测试 <!--显示控件--> <audio src="../images/wind.mp3" id="audioOne" contro ...

  4. keepalived+mysql双主复制高可用方案

    MySQL双主复制,即互为Master-Slave(只有一个Master提供写操作),可以实现数据库服务器的热备,但是一个Master宕机后不能实现动态切换.而Keepalived通过虚拟IP,实现了 ...

  5. Java之定时任务详解

    在我们编程过程中如果需要执行一些简单的定时任务,无须做复杂的控制,我们可以考虑使用JDK中的Timer定时任务来实现.下面就其原理.实例以及Timer缺陷三个方面来解析java Timer定时器. 在 ...

  6. SecureCRT使用的技巧 键盘修改

    secureCRT 修改PageUP,PageDown,Home,End键小trick:http://blog.csdn.net/shark_sq/article/details/6722512 所有 ...

  7. 显示Title和隐藏Title的ListView

    一.主要步骤 ①.调用ListView的addHeaderView(),创建一个与Title一样高的View,这样ListView就不会刚开始被遮盖了 ②.调用ListView的setOnTouchE ...

  8. python实现zabbix_sender的socket通信代码样例

    sk = socket.socket() sk.connect(self.ip_port) sk.settimeout(5) sk.sendall(b'ZBXD\x01') sk.sendall(b' ...

  9. TensorFlow 深度学习笔记 卷积神经网络

    Convolutional Networks 转载请注明作者:梦里风林 Github工程地址:https://github.com/ahangchen/GDLnotes 欢迎star,有问题可以到Is ...

  10. XML的Schema约束

    XSD文档至少要包含:schema根元素和XML模式命名空间的定义.元素定义.需要注意的是XSD中必须定义一个且只能定义一个schema根元素,根元素中包括模式的约束,XML模式命名空间的定义,其他命 ...