美文网首页
Runtime 源码解析之对象创建

Runtime 源码解析之对象创建

作者: Mt丶Jean | 来源:发表于2017-03-01 17:58 被阅读75次

    写在前面

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

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

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

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

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

    正文

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

    动态创建类

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

    运行起来会报以下错误:

    运行报错

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

    实现方法

    再次运行。。。

    未实现initialize方法

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

    实现initialize方法

    再run一次

    未实现alloc方法

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

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

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

    alloc方法

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

    callAlloc

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

    class_createInstance

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

    _class_createInstanceFromZone

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

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

    objc_constructInstance

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

    objc_object

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

    objc_object::initIsa

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

    isa_t

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

    alloc

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

    大功告成 运行结果

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

    相关文章

      网友评论

          本文标题:Runtime 源码解析之对象创建

          本文链接:https://www.haomeiwen.com/subject/omoqgttx.html