Objective-C 语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。
对于 Objective-C 来说,这个运行时系统就像一个操作系统一样:它让所有的工作可以正常的运行。Runtime 基本上是用 C 和汇编写的,这个库使得C语言有了面向对象的能力。
在 Runtime 中,对象可以用 C 语言中的结构体表示,而方法可以用 C 函数来实现,另外再加上了一些额外的特性。这些结构体和函数被 runtime 函数封装后,让 Objective-C 的面向对象编程变为可能。
找出方法的最终执行代码:当程序执行[object doSomething]
时,会向消息接收者(object)发送一条消息(doSomething),runtime 会根据消息接收者是否能响应该消息而做出不同的反应。
1 消息机制
与古老的 C 语言不同,Objective-C 虽然源自 C 语言,但是它却是面向对象的,在这之中,消息机制发挥着重大作用。
C语言和Objective-C编译时的区别:
C 语言在编译的时候,已经知道调用哪一个函数。
Objective-C 不一样,只有在运行时才知道需要调用的方法和函数。
1 |
|
使用这个方法要#import <objc/message.h>
,另外,Apple 在 Xcode5 开始,不建议使用底层方法,而恰巧以上方法就是底层方法。此时,Xcode 就会报错,那么,如何解决呢?
解决方案如下:
- 1)打开 Project 的
Build Settings
,搜索“msg”。 - 2)将
Enable Strict Checking of objc_msgSend Calls
的值设置为 NO。
发送无参消息
1 |
|
发送带参消息
1 |
|
附加:将Objective-C转换出Runtime代码方法
1 |
|
交换方法的实现
class_getInstanceMethod(__unsafe_unretained Class cls, SEL name)
获取对象方法。
class_getClassMethod(__unsafe_unretained Class cls, SEL name)
获取类方法。
method_exchangeImplementations(Method m1, Method m2)
交换方法的实现方式。
常见示例代码:
1 |
|
但url
可能返回nil,而此段代码未能对返回值url
进行合法判断。但每次使用以上类似代码都需要进行合法性判断,那么有什么更好的方法使URLWithString:
能够做合法性判断呢?这个时候就需要使用Runtime的特有方法了。
1 |
|
归档和解档
先来理解几个 Objective-C 中的概念:
序列化:将自定义的 Objective-C 的对象转化成二进制文件数据。
反序列化:将二进制文件数据转化成自定义的 Objective-C 的对象。
归档:将自定义的 Objective-C 的对象存储到本地磁盘。
解档:将存储在本地磁盘的数据转换成自定义的 Objective-C 的对象。
Ivar 类型:成员属性。
Method 类型:成员方法。
通常我们使用归档和解档的方式如下:
1 |
|
Objective-C 中归档底层实现方式:将对象拆分为字典(键值对),然后变成二进制存入磁盘。
但是,当 model 中成员属性数量很多的时候,就沦为了体力劳动。那么,此时我们又能使用 Runtime 来简化工作。
class_copyIvarList(__unsafe_unretained Class cls, unsigned int *outCount)
获取 Class 中成员变量的个数。
以下就是使用 Runtime 消息机制编写的归档与解档方法:
1 |
|
2 KVO
利用 Runtime,在运行时动态创建一个对象。
实现原理:
- 创建
NSKVONotifying_XXX:XXX
类(XXX为被监听者)。 - 重写属性 set 方法,调用
willChangeValueForKey:
和didChangeValueForKey:
方法,进而触发调用观察者的observeValueForKeyPath:ofObject:change:context:
示例代码:
1 |
|
3 动态添加方法
当方法被调用时,才被加载。
+ (BOOL)resolveClassMethod:(SEL)sel;
当一个类被调用了一个没有实现的方法时,则会调用此方法。
+ (BOOL)resolveInstanceMethod:(SEL)sel
当一个类被调用了一个没有实现的实例方法时,则会调用此方法。
class_addMethod(__unsafe_unretained Class cls, SEL name, IMP imp, const char *types)
动态添加方法。
参数cls
:类类型。参数name
:方法编号。参数imp
:方法实现,就是一个函数的指针。参数* types
:方法类型
1 |
|