iOS 开发之Method Swizzling

前言

如果你还不知道什么是Method Swizzling,你可以看看NSHipster 的文章 ,我简单介绍一下,method swizzling 可以看成劫持了一个方法。


我们可以看看NSHipster 的文章中有以下代码:

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

是不是觉得递归无限循环,事实上并不会,你可以这么理解,一个具体的SEL是个名字,一个具体的IMP是个函数指针,在类里面它们是靠一个表建立联系。


假设(纯属虚构) UIViewController 的 @selector(viewWillAppear:) 对应的内部实现为以下

void _UIKIT_Internal_UIViewController_viewWillAppear(id vc, SEL selector, BOOL animated) {...// ^_^苹果私有代码
}

假设UIViewController 的 @selector(xxx_viewWillAppear:) 的实现为以下

void my_xxx_viewWillAppear(id vc, SEL selector, BOOL animated) {[vc performSelector:@selector(xxx_viewWillAppear) withObject:@(animated)];  NSLog(@"viewWillAppear: %@", vc);
}

那么未替换方法前@selector(viewWillAppear:)对应的指针就是&_UIKIT_Internal_UIViewController_viewWillAppear

那么未替换方法前@selector(xxx_viewWillAppear:)对应的指针就是&my_xxx_viewWillAppear


一旦替换方法后,@selector(viewWillAppear:)对应的指针就是&my_xxx_viewWillAppear
@selector(xxx_viewWillAppear:)对应的指针就是&_UIKIT_Internal_UIViewController_viewWillAppear


my_xxx_viewWillAppear 中的 [vc performSelector:@selector(xxx_viewWillAppear) withObject:@(animated)];
相当于什么,相当于 调用@selector(xxx_viewWillAppear:)所指的函数&_UIKIT_Internal_UIViewController_viewWillAppear, 也就是相当于调用原来的函数,所以这并不是递归。


存在的危险

  1. 有些NSObject类是类簇,替换方法要找到真正的类,如NSArray替换方法,通常用'_NSArrayM'或'_NSArrayI'
  2. 执行顺序问题,ViewController是自己写的类,ViewController的load方法和UIViewController的category里的load方法哪个先执行,如果是ViewController的先执行,那么UIViewController的swizzling就对ViewController无效,如果ViewController的后执行就一切正常,代码如下
@implementation UIViewController (Tracking)+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{[self replaceOriginalSelector:@selector(viewWillAppear:) withNewSelector:@selector(xxx_viewWillAppear:)];});
}- (void)xxx_viewWillAppear:(BOOL)animated {[self xxx_viewWillAppear:animated];NSLog(@"x_viewWillAppear: %@", self);
}
@end@interface ViewController ()@end@implementation ViewController+ (void)load {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{[self replaceOriginalSelector:@selector(viewWillAppear:) withNewSelector:@selector(yyy_viewWillAppear:)];});
}- (void)yyy_viewWillAppear:(BOOL)animated {[self yyy_viewWillAppear:animated];NSLog(@"y_viewWillAppear: %@", self);
}@end

3.也是顺序问题
以下,来自另一篇文章defagos.github.io
它是这么说的,NSObject 实现了 -awakeFromNib,但是它的子类UIView,孙类UILabel都没有在本类实现- awakeFromNib,那么替换时如果不写在category +(void) load方法里,那么顺序也是个问题

假设我们先替换了UILabel的- awakeFromNib方法,然后再替换了UIView的- awakeFromNib,那么UILabel的- awakeFromNib方法就不会执行UIView的- awakeFromNib方法,因为它们都是独立替换的是NSObject方法,这篇文章defagos.github.io 提供了一个解决方案,当本类没有实现方法(如 UILabel -awakeFromNib)时,首先先添加一个block方法 调用[super awakeFromNib];,我初次看到也是觉得这个方式很好,但是我发现两点不足,

  1. 在构建objc_super时,如果父类本身没有实现方法,.super_class = class_getSuperclass(clazz),这么写好像是没有用的,似乎super_class要填真正实现方法的祖先类,而不能一概的写class_getSuperclass

    struct objc_super super = {.receiver = self,.super_class = class_getSuperclass(clazz)};
  2. va_list的使用
id (*objc_msgSendSuper_typed)(struct objc_super *, SEL, va_list) = (void *)&objc_msgSendSuper;

由于 va_list 变量最后是靠宏 va_arg(ap, type) 来获取的,type又是未知的,这个函数指针的强制转换转换可能会出现问题


结语

可见,Swizzling正确的顺序是十分重要的,(共同点:类本身没有真正实现方法)

  • 如果你的Swizzling是静态的,那么就保证父类的Swizzling发生在子类的Swizzling前
  • 如果你的Swizzling是动态的或者你管不好他们先后顺序,那么就要用defagos的方法动态的call objc_msgSendSuper,
  • 或许你勤快一点还可以直接在category里面重写,
    - (void)awakeFromNib {
    [super awakeFromNib];
    }

转载于:https://blog.51cto.com/14033137/2309805

iOS 开发:Method Swizzling相关推荐

  1. iOS黑魔法--Method Swizzling

    参考链接: iOS黑魔法-Method Swizzling iOS 黑魔法 - 专题 - 简书 Method Swizzle黑魔法,修改 ios 系统类库方法

  2. iOS 开发:『Runtime』详解(二)Method Swizzling

    本文用来介绍 iOS 开发中『Runtime』中的黑魔法Method Swizzling. 通过本文,您将了解到: Method Swizzling(动态方法交换)简介 Method Swizzlin ...

  3. 【原】iOS动态性(三) Method Swizzling以及AOP编程:在运行时进行代码注入

    概述 今天我们主要讨论iOS runtime中的一种黑色技术,称为Method Swizzling.字面上理解Method Swizzling可能比较晦涩难懂,毕竟不是中文,不过你可以理解为" ...

  4. iOS总结-Runtime篇之黑魔法Method Swizzling的滥用会有危险吗

    参考https://www.jianshu.com/p/19c5736c5d9a, http://blog.sina.com.cn/s/blog_a343f32b0101en4o.html runti ...

  5. 关于Swift4.0 Method Swizzling(iOS的hook机制)使用

    2019独角兽企业重金招聘Python工程师标准>>> 关于Method Swizzling 原理什么的有很多帖子讲述的已经很清楚这里不再赘述, 这里仅仅处理Method Swizz ...

  6. iOS总结-Runtime篇之用途及面试题的总结一黑魔法Method Swizzling

    runtime的用途基本分下面几类: 实现多继承Multiple Inheritance 上一篇里面的最后利用methodSignatureForSelector来进行一次转发,在forwardInv ...

  7. 【转】几点 iOS 开发技巧

    [译] 几点 iOS 开发技巧 原文:iOS Programming Architecture and Design Guidelines 原文来自破船的分享 原文作者是开发界中知晓度相当高的 Mug ...

  8. iOS 开发面试通关指南:67 个必知问题!

    作者 | Artur Rymarz 译者 | 香槟超新星,责编 | 郭芮 出品 | CSDN(ID:CSDNnews) 以下为译文: 我们都知道,面试可能会让人感到压力山大--不管你是第一次参加面试的 ...

  9. 唐巧的《iOS开发进阶》 - 读后感

    2019独角兽企业重金招聘Python工程师标准>>> 唐巧的<iOS开发进阶> - 读后感 为什么要看书 为什么要多看书呢? 在技术类书籍上,看书的目的,不是为了记住所 ...

  10. 【原】iOS开发进阶(唐巧)读书笔记(二)

    第三部分:iOS开发底层原理 1.Objective-C对象模型 1.1 isa指针 NSObject.h部分代码: NS_ROOT_CLASS @interface NSObject <NSO ...

最新文章

  1. 个人开发者即时到账收款方案 BufPay.com
  2. 三维点云分割综述(中)
  3. 巧用Ajax的beforeSend 提高用户体验--防止重复数据
  4. 数据库路由中间件MyCat - 使用篇(2)
  5. 无重复字符的最长子串—leetcode3
  6. 有限元python编程流行吗_Python进行有限元分析
  7. Cow Contest(POJ-3660 )
  8. Python中__init__和__del__方法介绍
  9. 下行物理信道rs_LTE下行物理信道与物理信号
  10. Angular导入导出Excel控件简介
  11. 无线网络密码破解方法
  12. 传奇服务器文件组成,【教程】传奇服务端(版本)的结构以及重要文件功能的概述-A02...
  13. Dreamweaver CS6 行为概述
  14. chromium 下载地址
  15. 最新北京人才公寓申请流程,技术员的福利~
  16. 百度没有文化(转载)
  17. Ubuntu+Sendmail+Dovecot+Openwebmail 邮件服务器搭建完全解决方案
  18. 如何在线赚钱:28 种真正的在线赚钱方式
  19. Linux搭建邮箱服务器
  20. butter滤波器是iir吗_MATLAB IIR滤波器设计函数buttord与butter

热门文章

  1. Java Web开发实战(二)—Tomcat安装及环境变量配置
  2. npm:no such file /usr/local/lib/node_modules/vue-cli/node_modules/get-stream
  3. php从入门到精通分享,PHP从入门到精通学习路线图分享
  4. Go github.com/e421083458/golang_common/lib
  5. $.each 中return问题
  6. 用PHP写Yaf:Yaf_Registry类
  7. plesk 打不开php,如何在 Plesk 中管理 PHP
  8. java1.8移除apt,java 1.8上的maven-enunciate-plugin现在从最新的JDK中删除了(即java注释处理工具)...
  9. OFFICE技术讲座:关于坐标、位置的术语说明
  10. 做出产品不难,做好产品必须高手