一、Runtime初探:揭开iOS动态特性的神秘面纱
1.1 什么是Runtime?
Runtime,说白了就是「运行时」。
你写Objective-C代码的时候,编译器会把你的方法调用转换成一条消息发送。谁接收这个消息?谁处理这个消息?这些都不是编译时决定的,而是程序跑起来之后,由Runtime动态决定的。
我刚开始接触iOS开发时,总觉得这玩意儿很玄乎。后来才明白,Runtime其实就是一套C语言写的库,它给Objective-C提供了动态能力。没有Runtime,Objective-C跟C语言就没啥区别了。
举个例子,你调用一个方法:
[self doSomething];
编译器不会直接生成跳转到doSomething函数的指令,而是生成这样的代码:
objc_msgSend(self, @selector(doSomething));
这就是Runtime的核心——消息发送机制。程序运行时,Runtime会根据self的类型,去查找doSomething对应的实现,然后执行它。
核心要点:Runtime是Objective-C动态特性的基石。它负责类的创建、方法的调用、消息的转发、动态添加方法等一切「运行时」行为。
1.2 Runtime的历史与演进
聊到Runtime的历史,得从上世纪80年代说起。
1986年,Brad Cox和Tom Love创造了Objective-C语言。他们给C语言加上了Smalltalk风格的消息传递机制,这就是Runtime的雏形。我记得第一次读到这段历史时,心里想:原来动态语言的思想这么早就有了。
1996年,苹果收购了NeXT,Objective-C和它的Runtime随之进入苹果生态。那时候的Runtime还很简陋,功能有限。
2006年,苹果发布了Objective-C 2.0,Runtime迎来了重大升级。引入了属性、快速枚举、垃圾回收等特性。不过垃圾回收后来被弃用了,取而代之的是ARC。
2007年,iPhone发布,Runtime开始在移动端大显身手。我那时候还在做桌面开发,完全没想到Runtime会在移动端发挥这么大的作用。
2014年,Swift诞生了。很多人以为Runtime要凉了,但苹果在Swift中依然保留了与Objective-C Runtime的互操作性。说白了,Swift的很多动态特性,底层还是靠Runtime实现的。
| 时间 | 版本 | 重要变化 |
|---|---|---|
| 1986年 | Objective-C 1.0 | 诞生,引入消息传递机制 |
| 1996年 | NeXT Runtime | 进入苹果生态 |
| 2006年 | Objective-C 2.0 | 属性、快速枚举、垃圾回收 |
| 2007年 | iPhone Runtime | 移动端适配 |
| 2014年 | Swift Runtime | 与ObjC Runtime互操作 |
1.3 Runtime在iOS开发中的核心地位
你可能会问:Runtime到底有多重要?
这么说吧,iOS开发中很多「黑魔法」都离不开Runtime。我这些年做项目,几乎每个大型App都会用到Runtime的特性。
第一,消息转发机制。 这是Runtime最核心的能力。当对象收到一个无法处理的消息时,Runtime不会直接崩溃,而是给你三次机会去处理:
resolveInstanceMethod:— 动态添加方法forwardingTargetForSelector:— 转发给其他对象forwardInvocation:— 完整的消息转发
我曾经在项目中用消息转发实现了一个「万能代理」模式,一个对象可以响应多个协议的方法,代码量减少了40%。
第二,Method Swizzling。 说白了就是交换两个方法的实现。这在AOP(面向切面编程)中非常有用。比如你想给所有UIViewController的viewDidLoad方法加上日志统计,用Swizzling一行代码都不用改。
避坑指南:我曾经在线上项目中使用Method Swizzling,结果因为调用顺序问题导致崩溃。后来我养成了一个习惯:Swizzling操作一定要在+load方法中执行,并且使用dispatch_once保证只执行一次。
第三,动态添加属性和方法。 利用class_addMethod和objc_setAssociatedObject,你可以在运行时给已有的类添加新的行为。这在写Category时特别有用。
// 动态添加属性
static char kAssociatedKey;
objc_setAssociatedObject(self, &kAssociatedKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
// 动态添加方法
class_addMethod([self class], @selector(newMethod), (IMP)myNewMethod, "v@:");
第四,KVO和KVC。 KVO的底层实现就是Runtime。当你对一个属性添加观察时,Runtime会动态创建一个子类,重写属性的setter方法。嗯,这里要注意:KVO的性能开销不小,不要滥用。
第五,JSON转Model。 几乎所有iOS的JSON解析库(如MJExtension、YYModel)都依赖Runtime。通过class_copyIvarList获取类的所有属性,然后动态赋值。我自己也写过一套JSON解析工具,用Runtime遍历属性,配合KVC赋值,代码非常简洁。
个人经验:我建议每个iOS开发者都去读一读Runtime的源码。苹果开源了objc4库,里面包含了Runtime的完整实现。读完之后,你对Objective-C的理解会上一个台阶。
1.4 Runtime知识体系总览
为了让你对Runtime有一个整体的认识,我画了一张结构图。这张图涵盖了Runtime的核心知识点和它们之间的关系。
这张图展示了Runtime的四大核心领域:消息机制、类与对象结构、动态特性、实际应用场景。每个领域之间相互关联,共同构成了Runtime的完整知识体系。
我个人觉得,学习Runtime最好的方式就是「动手」。写几个Demo,试试Method Swizzling,试试动态添加方法,试试消息转发。只有亲手踩过坑,才能真正理解Runtime的精髓。
嗯,这一章就到这里。Runtime的世界很大,我们慢慢探索。
公众号:蓝海资料掘金营,微信deep3321