runtime methedSwizzling方法交换深入浅出,深刻分析NSArray的safe方法能否替换系统方法

1.声明: 学习底层的过程不止是装逼,更多的是学以致用,举一反三。
               本文观点均为个人观点,不对之处,欢迎指教。

一:什么是方法交换。

1.图中我们假设这是ViewController类的数据结构图,中分别解释下图中的A,B,C.
A:就是ViewController类。
B:  里面藏着ViewController分类的方法列表。
C:ViewController宿主类(不是分类)的方法列表。
图中我们知道,分类的方法列表和宿主类的方法列表存储的位置是不一样的。

2.就methodList内部是什么,我们分析下。


我们说函数的四要素,如图所示。这四个东西唯一决定了一个函数。
现在原理明白了,那么我们运行时交换函数的方法名称,那么其余不变。就实现了方法交换。

3.综上,我们给方法交换下个定义。
  3.1 在同一类(红色,同一类,后面阐述)中,我们通过运行时,通过两个方法选择器(@selector())找到对应的method,最后对method进行交换的过程。

二:我们就实际探讨下methedSwizzling在项目开发中的实际用处。
     用处:若您第一次听说,肯定觉得,有毛病吧,我写个方法,实现功能,为啥要替换?
     下面我提几个场景:
     1.许多app会有皮肤设置,我们对UIView的 setBackgroundColor方法交换,通过不同的皮肤设置不同的背景色。
     2.在AFNetworking中也有应用,AFN中利用runtime将访问网络的方法做了替换,替换后可以监听网络连接状态
    3.统计,特殊场合,自己想想。。 看来方法交换用处挺多。


三:我们要怎么进行方法交换才是最好的。
    相信你肯定听过Load方法
    1.说下load方法的特性;
    load方法是main函数执行之前就调用了。
    如果在类与分类中都实现了 load 方法,它们都会被调用,不像其它的在分类中实现的方法会被覆盖,这就使 load 方法成为了方法调剂的绝佳时机。

四:那就举个例子吧;

我们对ViewDidLoad方法替换成swiz_ViewDidLoad,此时打印的是swiz_ViewDidLoad方法,view的背景色是红色。

五:开发中我们都用过数组,字典的安全方法,也就是防止判断nil对象的插入,数组的越界引起的奔溃。
  1.于是项目中分类是这么写的。
   
2.使用的时候,你是这么用的
  
3.相信这样的代码比比皆是对吧。

 思考:如果我们把cd_safeObjectAtIndex和系统的ObjectAtIndex方法替换,那么我们表面上用的还是ObjectAtIndex,其实内部实际实行的是cd_safeObjectAtIndex的安全方法。
  多么美好的想法。
  我们把addObject, objectAtIndex,insertObject:atIndex:,removeObjectAtIndex:全部替换。
 再也不用担心数组,字典的越界等问题了。

六:说到我们就开始行动,对数组的分类方法,进行交换
1.我们创建数组的safe分类,在load方法中进行方法交换
    

解释一下:
A: 这里我们分别对NSArray类的ObjectAtindex:和safeObjectAtIndex:方法进行了交换。
B:如果有值,调用safeObjectAtIndex:此时你可能会怀疑,额这里不是进行了循环调用了吗?
    回答是:不会的,此时你调用safeObjectAtIndex:实际是调用ObjectAtindex:方法了


好:我们创建了一个空数组,然后打印数组的第二个元素。毫无疑问,这里会数组越界引起奔溃。
      但是由于我们,进行了方法交换,理论上,这里是不会奔溃的。
   那就运行一下吧。


mmp,奔溃了。
插曲:先想一想,按照上面的逻辑,这里是不应该奔溃的
查看奔溃日志
** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 2 beyond bounds for empty NSArray’



奔溃我明明创建的是NSArray的初始化方法,怎么奔溃打印的是__NSArray0这个类呢。


2.这个问题我们先放下:对数组的分类,方法交换是NSArray修改__NSArray0类型


此时我们在看打印结果

额:效果实现了,数组明明是越界了,但是我们替换了安全的方法,返回了空,越界也不可怕了。
  wow大功告成,原来这么简单啊。
  希望你继续看下去??

2.那我们就多试试,看看是不是万无一失;
 我们换下数组arr的初始化方法
此时还会数组越界吗?运行一下试试。

   mmp,又奔溃。说现在创建的数组是 __NSSingleObjectArrayI类型。。
  
  我必须改成不奔溃,暴脾气

运行,
如我们想象的一样,不崩溃了,确实打印了数组越界,但是走了安全的方法,大功告成

七:通过六的例子,我们不得不陷入深深的思考。为什么会这样。

  1.查看资料,我们引入一个概念,叫做类簇
   类簇:先不解释,说下大家都熟悉的工厂设计模式
   工厂设计模式:比如福特汽车工厂:
                            他能生产福克斯,蒙迪欧,锐界,翼虎,野马类型的轿车
   
                            NSArray工厂,能生产__NSSingleObjectArrayI,__NSArray0的类型的数组
 多么清新脱俗的比喻,简单粗暴,易于理解。

 类簇
在iOS的Foundation框架中,类簇是一种常用的设计模式,他将一些相近的,私有的,具体的子类组合在一个实体的抽象类下面,我称这个抽象类为实体的,是因为和我们交互的接口承载者,就是这个抽象大类。我们平时常用的三大类,NSString,NSArray,NSDictionary都是类簇,我们通过他们创建的对象都是其子类对象的实例化,并不是他本身的实例化。就等于,我从福特公司买了一辆轿车,是福特产的,但是具体买的却是蒙迪欧。
关于类簇的理解,工厂设计模式,你可以看这篇文章。

现在我们看下之前我给方法交换的定义:
同一类(红色,同一类,后面阐述)中,我们通过运行时,通过两个方法选择器(@selector())找到对应的method,最后对method进行交换的过程。
我特别强调了同一类:那以后我们开发中,如果我们方法交换的前后类都是同一个类,那么可以放心使用。
 那如果是NSString,NSArray,NSDictionary等抽象类,
 打死不能方法交换, 打死不能方法交换, 打死不能方法交换
不然真会出现一些你排查不到的问题,慎用慎用。

七:我们看下一个封装很好的例子,就是对NSString,NSArray,NSDictionary安全方法替换的博客。

这是我从网上看到对数组安全方法的方法交换封装的github

初次看到,感觉我一直想搞的事情,终于有前人已经封装好了。

内部实现也就是:对于不同初始化的NSArray的方法,类名都进行了一一修改,可以说是很用心,用的话,也大致都满足开发需求了


但是我没看到__NSArray0类型的相关转化,所以当我用NSArray *arr = @[@“1"];初始化时,项目就奔溃了

八:关于methodSwizzling的思考:
   一开始:我说用于统计,这场景没遇到过,但是确实是一个极佳的例子,这里记录下。

      当你对一个知识点,进行了深入的理解之后。那么如何使用,如何闭坑。也就玩的游刃有余了。

       使用 Method Swizzling 编程就好比切菜时使用锋利的刀,一些人因为担心切到自己所以害怕锋利的刀具,可是事实上,使用钝刀往往更容易出事,而利刀更为安全。

      勉励:如果我想学习一个东西,请坚持一下几点
      1.这东西是什么?
      2.这东西怎么用?在哪里用最好?
      3.这东西开发中的实际用处?
      4.这东西有什么坑,要注意什么?
      5.举一反三。

如果你喜欢这篇文章,或者有任何疑问,可以扫描第一个二维码,加楼主好友哦

也可以扫第二个二维码,关注楼主个人微信公众号。这里有很多生活,职业,技术相关的文章哦。欢迎您的到来。

微信号:                                             公众号


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页