A Waltz with Objc Runtime I

986 阅读3分钟

Objective-C 中,最令我着迷的就是他那高度灵活的 Runtime 机制。通过 Runtime 我们可以调用系统私有 API、修改第三方库、让功能实现的更简练,甚至可以扩展这门语言。

当然 Runtime 同时也是一柄双刃剑,若使用不慎,害人害己。

于是,我又开了这么一个系列(我之前挖下的坑会填的),会写一些相对较短篇幅的文章。介绍一些我们礼物说开发过程中使用的各种怪招,以及我平时的一些小实验。

BTW: 下面有一个投票,希望大家投一下。

场景一:组合优于继承

在 Swift 中,我们有非常优雅的解决方案:Protocol Extension。顾名思义,我可以给 Protocol 添加默认实现,于是很多原来需要继承的地方,可以由大量的 Protocol 来组成。

Swift 官方的 protocol oriented programming 也是因为这个而来。它在 Swift 的 标准库中大量使用。我们可以发现,Swift 标准库中的 Class 屈指可数,全都是 Struct + Protocol 来实现的。

然而,回到 Objective-C。很残酷的现实,这个 feature 并不存在。但在很多时候,我们有一些方法需要公用,但无法用继承解决。

例如想做页面访问统计,我们有一坨代码是共有的。然而,世界最遥远的距离发生了,BaseViewController 继承于 UIViewController; 而 BaseTableViewController 继承自 UITableViewController。

难道我们要含泪把同样的代码抄几遍么?或者重新把 BaseTableViewController 用 BaseViewController 实现一下么?

这个时候 OC Runtime 就出现了。

场景二:面对无法抄袭的代码

我们今天下班前在讨论一个问题:能不能复用 ViewController 的侧划返回 UIScreenEdgePanGestureRecognizer 的部分方法。

这个实现我们看不到,没法复制粘贴,于是能不能通过 Runtime 的方式。找 UIKit 借用一下?

你说什么,我偷实现?盗用 IMP 不能算偷……盗用 IMP!……程序汪的事,能算偷么?

Runtime 精简版概念

Objective-C 运行的时候,和其他的静态语言不同。实例变量 A 调用 B 方法,并不是 A 直接调用 B 的函数地址。而是先去拿着 B 去查表找到对应的地址,再去调用对应函数。

而那张表是可以在运行的时候改的..

名词解释 Class、SEL、IMP、Method

  1. 其中 Class 大伙儿再熟悉不过,就是标准的对象。
  2. SEL 实际上就是一个 C 字符串。我们可以通过 SEL 从 Class 中拿到后面要介绍的 Method。
  3. IMP 是函数的具体实现,可以理解为函数指针。
  4. Method 要重点说,这个是沟通 Class 和 IMP 的桥梁。它记录了:SEL、IMP、参数类型、参数个数、返回值类型等等。

大概关系是这样的,灵魂画师 txx 又上线了:


所以我们可以发现,IMP 是可以复用的,于是我们能乱搞了。

代码实现

为了高亮和清晰这里选用截图实现。


后记

上面说了很多,代码却很简单,当然这只是个 demo,经过封装可以做得更好。

最近这几周去了一趟大连、去了一趟日本、又回了一趟广州、参加了一次梁杰组织的线下聚会。再加上我书房因为漏电检修,被整个拉闸。停更了好久,抱歉了,我会补回来的。

不过这里有一个关于一周一算法的思考,如果我继续这么讲下去会不会节奏太快?经过我最近的面试反馈,感觉绝大多数程序员别说算法了,稍微复杂点的 For 循环都写不利索。所以想问大家的喜好,是希望我继续这样讲呢?还是节奏放慢,通过一些 OJ 上的题目来讲解呢?