iOS runtime 机制

说到 iOS runtime 机制,首先就需要了解 Objective-C 语言的起源。
Objective-C 是在 C 语言的基础上添加了面向对象特性,但是 C 语言是一门静态语言,而 Objective-C 是一门动态语言。
静态语言会在编译时就会决定运行时所应调用的函数方法,而动态语言只有到了运行时才会确定要执行的函数方法。而 runtime 机制就是 Objective-C 的动态语言特性的体现。

通常在调用方法函数时,我们会采用如下实现:

SomeObject *object = [[SomeObject alloc] init];
[object doSomething];

我们也可以这样写:

SomeObject *object = [[SomeObject alloc] init];
[object performSelector:@selector(doSomething)];

然而实现第一种调用方法的前提是,需要在接口文件 SomeObject.h 声明 doSomething 这个函数方法:

/**
 测试函数
 */
- (void)doSomething;

如果这个方法没有在接口文件被声明的话,第一种调用方式在编译的时候就会报错。相对的,第二种调用方式在编译时,虽然 Xcode 会多了个警告提示,提示说这个方法未被声明,但是编译还是会成功。

讲到这里,有人可能会认为第一种调用方式只对已经声明的方法进行调用,是不是说明了 Objective-C 也是一门静态语言呢?
那么我们现在就来做一个实验,把实现文件 SomeObject.mdoSomething 的定义方法给注释掉:

///**
// 测试函数
// */
//- (void)doSomething {
//    
//    NSLog(@"Runtime 测试");
//}

然后编译,这个时候会看到 Xcode 多了个警告提示,说没有找到 doSomething 的方法定义,但是编译还是成功了。可是,当程序在运行过程中,执行到调用 doSomething 的代码时,程序闪退了。
这个实验说明,编译器在编译时并没有去确定 doSomething 的方法定义是否有被实现,只有到了运行的时候才会寻找这个实现方法。所以这种调用方式说明了 Objective-C 是一门动态语言。

那么如果使用可以不用在接口文件声明方法的第二种调用方式,在运行时被执行到又会怎么样呢?
其实和分析第一种调用方式要考虑的东西是一样的,实现文件 SomeObject.m 是否定义了 doSomething 方法。
如果没有找到定义方法,程序会在执行到调用代码时闪退。相对的,如果找到定义方法,程序会执行 doSomething 的方法内容。

其实无论是要声明的第一种调用方式,还是可以不用声明的第二种调用方式,编译器在编译时,都会转换成一条标准的 C 语言函数调用。

objc_msgSend(object, @selector(doSomething));

objc_msgSend 函数会依据接收者 object 与 选择子 @selector(doSomething) 的类型来调用适当的方法。该方法会在接收者所属的类中搜寻其“方法列表”,如果能找到与选择子名称相符的方法,就跳至其实现代码。找不到的话,程序就会闪退。

那么如果我们想要在不闪退的情况下,要怎么确定某个方法的定义是被实现呢?
答案是

- (BOOL)respondsToSelector:(SEL)aSelector;

例子:

if ([object respondsToSelector:@selector(doSomething)]) {
        
   [object performSelector:@selector(doSomething)];
}

了解 iOS runtime 机制让我们能够了解 Objective-C 底层的工作原理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容