写在前面
稍微了解OC运行时机制的人应该都知道:
1. 类和对象都是id,在给你一个id的前提下无法直观的知道这个对象是类对象还是类本身。简单的可以简化成runtime管理的都是id (id的本质其实是objc_object, objc_class头部其实就是id, 也就是isa)。
2. Class在objc中是动态创建的,selector, method, imp,protocol等都是随后绑定上去的(即所谓的运行时绑定)。
3. 通过runtime能够查出当前运行时环境中所有的类,每个类中的方法,每个类消息的绑定,每个类的实现的协议,每个协议的定义,每个类当前的消息缓存等一切你想知道的东西。
4. 类的方法(消息)调用是间接的。
正文
好的,在我们知道上述知识以后。我们来研究下,在不存在NSObject的情况下,怎样动态创建一个类。
![](https://img.haomeiwen.com/i961290/cf6a4f4a6717b1e8.png)
图中几个方法的用法和作用不再赘述,不清楚的可以自行查阅官方文档。有一点需要注意,我们全程都不能使用 ARC,因为 ARC 模式下从void *转换到id是需要有一个 bridge 的过程,而这个过程仍然依赖于NSObject完成,所以我们又会陷入一个需要NSObject的死循环。
运行起来会报以下错误:
![](https://img.haomeiwen.com/i961290/f0710e4ae0a7701b.png)
查看文档得知,每当一个无效Selector被发出并没有得到响应,运行时系统将调用doesNotRecognizeSelector:,该方法执行后会引发NSInvalidArgumentException,并生成错误消息。那好吧,check一下。
![](https://img.haomeiwen.com/i961290/fa545ad09d996f42.png)
再次运行。。。
![](https://img.haomeiwen.com/i961290/6243e1850acaadae.png)
我们似乎忘了实现initialize方法。。。该方法会在某个类第一次被初始化时调用,加上它。
![](https://img.haomeiwen.com/i961290/897c3ec0a1a8885b.png)
再run一次
![](https://img.haomeiwen.com/i961290/f71df6cae00b5977.png)
额,alloc类方法是如何实现的?这个时候,我们就需要下载Runtime源码一探究竟了。。。
我们知道,在OC中,任何类都是继承自NSObject这一基类。这么一说,alloc类方法应该也是NSobject实现的。
我们打开源码,,找到NSObject.mm这个文件,并通过导航菜单快读定位到alloc方法的位置
![](https://img.haomeiwen.com/i961290/0e65675746f7b2cb.png)
按住 ⌘ 一路点击跟踪,直到这里:
![](https://img.haomeiwen.com/i961290/98ae21abcb6707e9.png)
我们发现,在Objc2.0之后新增了一种自定义的快捷构造方法。但是殊途同归,事实上它们最终都要调用class_createInstance这个方法,我们来看看:
![](https://img.haomeiwen.com/i961290/68d8b1270e197e09.png)
在老的源码中,这里会判断是否使用GC。iOS上是不能使用GC的,这是为Mac设计的,既然新版本去掉这个判断,我们也就没有必要去理会了。继续跟踪,我们来到了_class_createInstanceFromZone函数:
![](https://img.haomeiwen.com/i961290/bae057a0b1d384fd.png)
在这里,runtime主要做了有关内存对齐的一些计算,由于zone是nil,这里直接用calloc申请了一块内存。calloc 与 malloc 的区别,可以自行谷歌。
好的,接下来就剩最后一步了,objc_constructInstance函数:
![](https://img.haomeiwen.com/i961290/05f7bc698cc95a13.png)
这里回归了我们一开始说的,对象的isa指针。我们也说了,id的本质其实是objc_object,那么objc_object到底是什么:
![](https://img.haomeiwen.com/i961290/1d9615a4a3aa6721.png)
objc_object其实就是一个C++结构体,有权限控制,有成员函数。通过我们刚才提到的objc_object::initIsa函数,可以知道,就是把对象的isa指针指向这个类的元信息Class。
![](https://img.haomeiwen.com/i961290/9d6873d83cd7e36e.png)
看到这里,我们其实就已经可以为我们的类写一个alloc方法了。但是似乎有个问题无法解决,objc_object对于开发者而言并不能接触到,我们有必要通过直接修改内存的方式去修改其isa变量。那么,回到源码,我们看看isa那个isa_t类型究竟是什么:
![](https://img.haomeiwen.com/i961290/dc00e0e53279b046.png)
可以发现,它是个联合体。从源码中我们也可以看到,它在初始化的时候直接被当做uintptr_t对待,而uintptr_t又是unsigned long的typedef,所以我们可以写出如下代码:
![](https://img.haomeiwen.com/i961290/23fe32ee5d20fbfe.png)
好了,把实现的alloc方法添加到我们的类中,然后用初始化一个实例对象,再执行。
![](https://img.haomeiwen.com/i961290/5f76eda75e5b5e33.png)
![](https://img.haomeiwen.com/i961290/c8c55b73cbec49cf.png)
到此为止,在不存在NSObject的情况下,动态创建一个类的工作完成了。
网友评论