Runtime 源码解析之对象创建

写在前面

稍微了解OC运行时机制的人应该都知道:

1. 类和对象都是id,在给你一个id的前提下无法直观的知道这个对象是类对象还是类本身。简单的可以简化成runtime管理的都是id (id的本质其实是objc_object, objc_class头部其实就是id, 也就是isa)。

2. Class在objc中是动态创建的,selector, method, imp,protocol等都是随后绑定上去的(即所谓的运行时绑定)。

3. 通过runtime能够查出当前运行时环境中所有的类,每个类中的方法,每个类消息的绑定,每个类的实现的协议,每个协议的定义,每个类当前的消息缓存等一切你想知道的东西。

4. 类的方法(消息)调用是间接的。

正文

好的,在我们知道上述知识以后。我们来研究下,在不存在NSObject的情况下,怎样动态创建一个类。

Runtime 源码解析之对象创建_第1张图片
动态创建类

图中几个方法的用法和作用不再赘述,不清楚的可以自行查阅官方文档。有一点需要注意,我们全程都不能使用 ARC,因为 ARC 模式下从void *转换到id是需要有一个 bridge 的过程,而这个过程仍然依赖于NSObject完成,所以我们又会陷入一个需要NSObject的死循环。

运行起来会报以下错误:

运行报错

查看文档得知,每当一个无效Selector被发出并没有得到响应,运行时系统将调用doesNotRecognizeSelector:,该方法执行后会引发NSInvalidArgumentException,并生成错误消息。那好吧,check一下。

Runtime 源码解析之对象创建_第2张图片
实现方法

再次运行。。。

未实现initialize方法

我们似乎忘了实现initialize方法。。。该方法会在某个类第一次被初始化时调用,加上它。

Runtime 源码解析之对象创建_第3张图片
实现initialize方法

再run一次

未实现alloc方法

额,alloc类方法是如何实现的?这个时候,我们就需要下载Runtime源码一探究竟了。。。

我们知道,在OC中,任何类都是继承自NSObject这一基类。这么一说,alloc类方法应该也是NSobject实现的。

我们打开源码,,找到NSObject.mm这个文件,并通过导航菜单快读定位到alloc方法的位置

Runtime 源码解析之对象创建_第4张图片
alloc方法

按住 ⌘ 一路点击跟踪,直到这里:

Runtime 源码解析之对象创建_第5张图片
callAlloc

我们发现,在Objc2.0之后新增了一种自定义的快捷构造方法。但是殊途同归,事实上它们最终都要调用class_createInstance这个方法,我们来看看:

class_createInstance

在老的源码中,这里会判断是否使用GC。iOS上是不能使用GC的,这是为Mac设计的,既然新版本去掉这个判断,我们也就没有必要去理会了。继续跟踪,我们来到了_class_createInstanceFromZone函数:

Runtime 源码解析之对象创建_第6张图片
_class_createInstanceFromZone

在这里,runtime主要做了有关内存对齐的一些计算,由于zone是nil,这里直接用calloc申请了一块内存。calloc 与 malloc 的区别,可以自行谷歌。

好的,接下来就剩最后一步了,objc_constructInstance函数:

Runtime 源码解析之对象创建_第7张图片
objc_constructInstance

这里回归了我们一开始说的,对象的isa指针。我们也说了,id的本质其实是objc_object,那么objc_object到底是什么:

Runtime 源码解析之对象创建_第8张图片
objc_object

objc_object其实就是一个C++结构体,有权限控制,有成员函数。通过我们刚才提到的objc_object::initIsa函数,可以知道,就是把对象的isa指针指向这个类的元信息Class。

objc_object::initIsa

看到这里,我们其实就已经可以为我们的类写一个alloc方法了。但是似乎有个问题无法解决,objc_object对于开发者而言并不能接触到,我们有必要通过直接修改内存的方式去修改其isa变量。那么,回到源码,我们看看isa那个isa_t类型究竟是什么:

Runtime 源码解析之对象创建_第9张图片
isa_t

可以发现,它是个联合体。从源码中我们也可以看到,它在初始化的时候直接被当做uintptr_t对待,而uintptr_t又是unsigned long的typedef,所以我们可以写出如下代码:

Runtime 源码解析之对象创建_第10张图片
alloc

好了,把实现的alloc方法添加到我们的类中,然后用初始化一个实例对象,再执行。

Runtime 源码解析之对象创建_第11张图片
大功告成
运行结果

到此为止,在不存在NSObject的情况下,动态创建一个类的工作完成了。

你可能感兴趣的:(Runtime 源码解析之对象创建)