美文网首页iOS开发之深入理解runtimeRuntime源码iOS
iOS开发之runtime(4):浅析对象的创建

iOS开发之runtime(4):浅析对象的创建

作者: kyson老师 | 来源:发表于2018-12-03 18:29 被阅读16次

    本系列博客是本人的源码阅读笔记,如果有iOS开发者在看runtime的,欢迎大家多多交流。为了方便讨论,本人新建了一个微信群(iOS技术讨论群),想要加入的,请添加本人微信:zhujinhui207407,【加我前请备注:ios 】,本人博客http://www.kyson.cn 也在不停的更新中,欢迎一起讨论

    runtime logo

    在我们的main.m中输入以下代码:

    #import <Foundation/Foundation.h>
    
    int main(int argc, const char * argv[]) {
        NSObject *obj = [[NSObject alloc] init];
        return 0;
    }
    

    这段代码相信大家一眼就能看懂。那么,在代码

        NSObject *obj = [[NSObject alloc] init];
    

    执行的过程中究竟发生了什么,大家是否了解呢。本文就带大家研究一下alloc函数的实现过程。


    alloc函数

    进入 alloc函数,我们不难发现在文件NSObject.m中已经有其实现:

    + (id)alloc {
        return _objc_rootAlloc(self);
    }
    

    我们继续查看_objc_rootAlloc函数:

    id
    _objc_rootAlloc(Class cls)
    {
        return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
    }
    

    继续查看函数callAlloc

    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
        if (slowpath(checkNil && !cls)) return nil;
    
    #if __OBJC2__
        if (fastpath(!cls->ISA()->hasCustomAWZ())) {
            // No alloc/allocWithZone implementation. Go straight to the allocator.
            // fixme store hasCustomAWZ in the non-meta class and 
            // add it to canAllocFast's summary
            if (fastpath(cls->canAllocFast())) {
                // No ctors, raw isa, etc. Go straight to the metal.
                bool dtor = cls->hasCxxDtor();
                id obj = (id)calloc(1, cls->bits.fastInstanceSize());
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                obj->initInstanceIsa(cls, dtor);
                return obj;
            }
            else {
                // Has ctor or raw isa or something. Use the slower path.
                id obj = class_createInstance(cls, 0);
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                return obj;
            }
        }
    #endif
    
        // No shortcuts available.
        if (allocWithZone) return [cls allocWithZone:nil];
        return [cls alloc];
    }
    

    这段比较长,我们稍微分析一下:

    #if __OBJC2__
    用于判断objective-c 版本,是不是2.0,目前我们使用的objective-c版本都是此版本。

    slowpathfastpath
    其内部实现如下

    #define fastpath(x) (__builtin_expect(bool(x), 1))
    #define slowpath(x) (__builtin_expect(bool(x), 0))
    

    因此其实就是了解__builtin_expect这个指令:

    __builtin_expect这个指令是gcc引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:__builtin_expect(EXP, N)。
    意思是:EXP==N的概率很大。

    所以fastpath的含义是,为1的概率大,slowpath的含义是为0的概率大。

    知道了这几点,相信以上代码应该能大致读懂,将以上代码进行注释和省略部分宏定义如下:

    static ALWAYS_INLINE id
    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
        if (slowpath(checkNil && !cls)) return nil;
    //如果该对象没有自己的allocWithZone方法需要实现
        if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        //查看一下类是否能快速分配内存
            if (fastpath(cls->canAllocFast())) {
            //查看一下类是否有析构函数
                bool dtor = cls->hasCxxDtor();
                //分配内存,给obj对象
                id obj = (id)calloc(1, cls->bits.fastInstanceSize());
                //如果分配失败,那么交给错误处理
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                //初始化obj的isa
                obj->initInstanceIsa(cls, dtor);
                return obj;
            }
            else {
                id obj = class_createInstance(cls, 0);
                if (slowpath(!obj)) return callBadAllocHandler(cls);
                return obj;
            }
        }
    
        //如果allocWithZone 为true,则实现allocWithZone 方法
        if (allocWithZone) return [cls allocWithZone:nil];
        return [cls alloc];
    }
    

    看过笔者前面文章的读者应该能大致理解上面代码的含义了。主要运行了如下逻辑:

    1. 创建对象并分配内存
    2. 初始化isa属性

    创建对象这个不多说了,无非是获取对象大小,然后分配内存。初始化isa这一步其实还有一些逻辑,进入方法initInstanceIsa可以看到:

    inline void 
    objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
    {
        assert(!cls->instancesRequireRawIsa());
        assert(hasCxxDtor == cls->hasCxxDtor());
    
        initIsa(cls, true, hasCxxDtor);
    }
    

    继续进入方法:initIsa

    inline void 
    objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
    { 
        assert(!isTaggedPointer()); 
        
        if (!nonpointer) {
            isa.cls = cls;
        } else {
            assert(!DisableNonpointerIsa);
            assert(!cls->instancesRequireRawIsa());
            isa_t newisa(0);
    #if SUPPORT_INDEXED_ISA
            assert(cls->classArrayIndex() > 0);
            newisa.bits = ISA_INDEX_MAGIC_VALUE;
            newisa.has_cxx_dtor = hasCxxDtor;
            newisa.indexcls = (uintptr_t)cls->classArrayIndex();
    #else
            newisa.bits = ISA_MAGIC_VALUE;
            newisa.has_cxx_dtor = hasCxxDtor;
            newisa.shiftcls = (uintptr_t)cls >> 3;
    #endif
            isa = newisa;
        }
    }
    

    这里对以上代码做个初步分析:

    TaggedPointer
    暂不做介绍,大家可以先不用理解,后面的文章会给出分析。

    SUPPORT_INDEXED_ISA
    表示 isa_t 中存放的 Class 信息是 Class 的地址,还是一个索引(根据该索引可在类信息表中查找该类结构地址)。经测试,iOS 设备上 SUPPORT_INDEXED_ISA 是 0。

    因此以上代码可以简写成:

    inline void 
    objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
    { 
        assert(!isTaggedPointer()); 
        
        if (!nonpointer) {
            isa.cls = cls;
        } else {
            assert(!DisableNonpointerIsa);
            assert(!cls->instancesRequireRawIsa());
            //创建isa对象
            isa_t newisa(0);
            //设置占位bits
            newisa.bits = ISA_MAGIC_VALUE;
            //有无析构函数
            newisa.has_cxx_dtor = hasCxxDtor;
            //我们熟悉的存储对象类信息的字段
            newisa.shiftcls = (uintptr_t)cls >> 3;
            //赋值
            isa = newisa;
        }
    }
    

    init函数

    init函数的实现相对来说非常简单了,我们看一下他的调用栈:

    - (id)init {
        return _objc_rootInit(self);
    }
    

    继续分析_objc_rootInit

    id
    _objc_rootInit(id obj)
    {
        // In practice, it will be hard to rely on this function.
        // Many classes do not properly chain -init calls.
        return obj;
    }
    

    总结

    本文通过分析代码:

        NSObject *obj = [[NSObject alloc] init];
    

    带领大家分析了一下对象的创建过程,希望对大家理解对象创建有一定帮助。


    本文完整版详见笔者小专栏:https://xiaozhuanlan.com/runtime


    相关文章

      网友评论

        本文标题:iOS开发之runtime(4):浅析对象的创建

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