Effective Objective-C 2.0 Tip 13: method swizzling 应用场景

得承认,每每看到别人声称 method swizzling是一个多么强大的工具,我就很纳闷,这工具的应用场景有哪些?平常我找不到它的用处,看了好几次关于这个的文章,过不久忘光光。

首先,给出一些好文章:
1.Method Swizzling
2.Method Swizzling 和 AOP 实践
站在大神的肩上学习,也许,有可能,还是搞不清楚这玩意的强大。

回顾最近看英文文章和中文文章的过程,中文文章里出现模糊性的词汇就晕菜,比如上面这篇里提到破坏代码整洁性,而相应里英文文章里讲述同样的东西,只是简单地说:

Each view controller could add tracking code to its own implementation of viewDidAppear:, but that would make for a ton of duplicated boilerplate code.

其实上面的中文文章里也讲到了这个,同样还有一些其他理由,但是,出现了那个学院派的说法后,我对这些理由的理解就消逝了。也许我由于不是科班出身,没被这种词汇熏陶过,在我接触编程过程中总是由于这类词汇的出现而懵了。还有一点,文章一定要看写得好的,不管中文英文,我这种肯定不行。很多时候,看中文看不明白,去看英文原文就明白了,得习惯看英文。
这几天看 iOS 基础的东西写博客记录,发现自己以往学习东西的特点是,这东西不知道干啥的,学起来很没效率,当初我不知道算法的用处,学习的时候也没啥动力,学过就忘,可能我骨子里就不程序员吧,至少不是那种啊这个算法好有意思我要搞懂的那种。如今有点改变,因为发现自己不会的东西都很有用处,囧。

老实说,这俩篇看下来,我几乎就没啥可写的了,不过由于我原本在看这两篇的时候都有不理解的地方,这次看恰好互补了,那么就总结一下。

应用场景

Method Swizzling 看来就像一个魔法,效果就是在运行时把调用的方法调包。那么会有哪些应用场景呢?一般来说,想要在原来的方法里加点东西或是原来的方法不合适,我们需要牵一发动你全身的效果,一劳永逸,子类化重写的缺点是你没法控制其他地方使用你的子类,而 category 重写方法无法保证重写的方法被调用,《Avoid Category Method Name Clashes》里提到:

If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes.

那么 Method Swizzling 正是用来做这个的。显然这些都是针对Cocoa 里的类,有源码的类就不用费这么大劲了。
比如,我在调试 CoreData + UICollectionView 的时候,想要看到 UICollectionViewCell 的变化的具体信息,会打印 NSIndexPath 的信息,但默认的 description 方法不直观,使用 NSLog 得调整格式很繁琐,重写其 description 方法打印就好。

实现

Glow 博客写实现过程的细节更好理解,还特别贴心地解释了为何要先用 class_addMethod,看到 Matt 大神的文章这一块就很疑惑,为什么要先用 class_addMethod 而是不是直接使用 method_exchangeImplementations 呢?

使用 class_addMethod 的原因

看文档对与 class_getInstanceMethod 的解释,原来如此:

Note that this function searches superclasses for implementations, whereas class_copyMethodList does not.

对于在什么地方进行替换,Matt 的文章细节更多,终于对 +load 和 +initialize 这两方法有更多认识了,其实方法名就揭示了它们的用处。

+load vs. +initialize
Swizzling should always be done in +load.

There are two methods that are automatically invoked by the Objective-C runtime for each class. +load is sent when the class is initially loaded, while +initialize is called just before the application calls its first method on that class or an instance of that class. Both are optional, and are executed only if the method is implemented.

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.

对于这两个方法:

+ (void)load
  • 按官方文档的说法:
    1.每个类以及它的 category 添加到 runtime 时这个方法会被调用,且只调用一次。
    2.每个类的 +load 调用有顺序依赖,在其各个超类的 +load 调用结束后。(测试表明载入时,以类似深度优先的方式载入子类,如果子类还有子类,会把继续载入子类然后最后一个子类,然后再载入同层次的其他子类。)
    3.而 category 的 +load 调用则在类的 +load 调用之后。(如果不同层次的类有 category 实现了 +load,这个调用顺序是怎样呢?测试表明,全部 category 的+load 调用都在所有类的 +load 调用完毕后才调用,而不是调用类的 +load后然后去调用该类的 category 的 +load 方法再去调用其他类;再次 category 的 +load 调用顺序和其类的调用顺序无关,没有层级和先后关系。)
  • Tip 51 里面的 addtion:
    1.执行 +load 时,runtime 出于脆弱状态(到底怎么个脆弱法),在 load 中使用其他类不安全(就是这么个脆弱法,程序启动中其他类可能还没有 load)。
    2.+load 不像普通的方法遵守继承规则,普通的方法里,如果类没有实现该方法,就询问其超类,超类没有实现,继续向上询问,直到根类 NSObject,NSObject 也没有实现的话,进入消息转发流程。那么 +load 方法,该类没有实现这个方法的话,就不会调用这个方法。
+ (void)initialize
  • 官方文档 + Tip51
    该方法在程序中第一次向该类或者该类的子类发送消息的时候调用,且也只调用一次(类本身也是对象,使用前也要初始化,只不过类都是单例对象)
    这里容易让我疑惑,文档里这么说的:

The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program.

首次使用某个类,这个类会收到 +initialize 消息,假设之前其超类尚未被初始化过,这时候其超类也会收到 +initialize 消息吗?

The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize].

会出现该类初始化了,但其超类没有初始化的情况吗?(假如该类实现了 +initialize 方法,因此不用调用其超类的)
但实际情况是,往往我们使用某个子类时,生成子类的实例对象必然会调用其超类生成实例对象的方法,必然会使用该类,于是必然对超类进行初始化。对于同级别的子类,比如,某类有三个子类,使用其中一个子类时,该子类及其父类都会收到 +initialize 消息,但另外两个子类不会收到该消息。只有使用另外两个子类时,才会收到 +initialize 消息,这是惰性调用,其父类是否会收到该消息则根据上面的引用描述的那样。这么一来,文档中说的,每个类只调用一次的说法有点问题啊。实际上这句话的意思是,使用该类生成多个实例时,+initialize 方法只会调用一次。

两个方法对比:
1.+load 不支持继承,类没有实现就不会调用;而 +initialize 支持,类没有实现就调用父类的。
2.+load 在类载入的时候由 runtime 自动调用;而 +initialize 是惰性调用,使用类的时候才调用。
3.+load 只调用一次,而 +initialze 方法,虽然对每个类来说也只会自动调用一次(无论生成多少实例对象,该方法只调用一次),但情况比较复杂:1.使用子类的时候如果没有实现该方法,会调用父类的;2. 子类里显式调用父类的方法。所以 Matt 大神强烈建议在 +load 里使用 method swizzling。

另外,《Objective-C Class Loading and Initialization》值得一看。这两方法也是《iOS面试基础题目》中 class 载入以及 category 实现机制的重要基础。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容