一、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不会直接崩溃,而是给你三次机会去处理:

  1. resolveInstanceMethod: — 动态添加方法
  2. forwardingTargetForSelector: — 转发给其他对象
  3. forwardInvocation: — 完整的消息转发

我曾经在项目中用消息转发实现了一个「万能代理」模式,一个对象可以响应多个协议的方法,代码量减少了40%。

第二,Method Swizzling。 说白了就是交换两个方法的实现。这在AOP(面向切面编程)中非常有用。比如你想给所有UIViewControllerviewDidLoad方法加上日志统计,用Swizzling一行代码都不用改。

避坑指南:我曾经在线上项目中使用Method Swizzling,结果因为调用顺序问题导致崩溃。后来我养成了一个习惯:Swizzling操作一定要在+load方法中执行,并且使用dispatch_once保证只执行一次。

第三,动态添加属性和方法。 利用class_addMethodobjc_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核心 消息发送与转发机制 objc_msgSend 消息转发 SEL与IMP 类与对象结构 isa指针 元类 动态特性 Method Swizzling 关联对象 实际应用场景 KVO/KVC JSON转Model AOP编程 热修复 理解Runtime = 掌握iOS动态编程的核心

这张图展示了Runtime的四大核心领域:消息机制、类与对象结构、动态特性、实际应用场景。每个领域之间相互关联,共同构成了Runtime的完整知识体系。

我个人觉得,学习Runtime最好的方式就是「动手」。写几个Demo,试试Method Swizzling,试试动态添加方法,试试消息转发。只有亲手踩过坑,才能真正理解Runtime的精髓。

嗯,这一章就到这里。Runtime的世界很大,我们慢慢探索。


公众号:蓝海资料掘金营,微信deep3321