美文网首页
Runtime-类

Runtime-类

作者: Joolybgo | 来源:发表于2018-10-10 16:44 被阅读23次

    这里会把类相关、程序启动类信息填充、引用计数都会讲下。想要深入了解OC的动态性,就必须去研究runtime的代码,所幸它是开源的,你可以在源码下载不同的版本。

    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    
    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
        class_rw_t *data() {
            return bits.data();
        }
        ....省略
     }
    
    struct objc_object {
    private:
        isa_t isa;
    public:
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
        ....省略
    }
    

    根据上面的声明,凡是首地址是isa,都可以被认为是objc中的对象。运行时可以通过isa获取到该对象是属于什么类(Class)。那类对象有所属的类吗?有,称之为元类(Metaclass)。

    class_isa_image

    上面这张图可以把isasuperclass表现的很清楚。

    结构体 isa_t

    接下来看下isa的结构:

    #if (!__LP64__  ||  TARGET_OS_WIN32  ||  TARGET_OS_SIMULATOR)
    #   define SUPPORT_PACKED_ISA 0
    #else
    #   define SUPPORT_PACKED_ISA 1
    #endif
    
    union isa_t 
    {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    
    #if SUPPORT_PACKED_ISA
    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
        struct {
            uintptr_t nonpointer        : 1;
            uintptr_t has_assoc         : 1;
            uintptr_t has_cxx_dtor      : 1;
            uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
            uintptr_t magic             : 6;
            uintptr_t weakly_referenced : 1;
            uintptr_t deallocating      : 1;
            uintptr_t has_sidetable_rc  : 1;
            uintptr_t extra_rc          : 19;
    #       define RC_ONE   (1ULL<<45)
    #       define RC_HALF  (1ULL<<18)
        };
    
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    #   define ISA_MAGIC_MASK  0x001f800000000001ULL
    #   define ISA_MAGIC_VALUE 0x001d800000000001ULL
        struct {
            uintptr_t nonpointer        : 1;
            uintptr_t has_assoc         : 1;
            uintptr_t has_cxx_dtor      : 1;
            uintptr_t shiftcls          : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
            uintptr_t magic             : 6;
            uintptr_t weakly_referenced : 1;
            uintptr_t deallocating      : 1;
            uintptr_t has_sidetable_rc  : 1;
            uintptr_t extra_rc          : 8;
    #       define RC_ONE   (1ULL<<56)
    #       define RC_HALF  (1ULL<<7)
        };
    
    ...省略
    #if SUPPORT_INDEXED_ISA
    ...
    

    isa指针是一个联合体,在不同环境下,结构体中属性值稍有差别。

    • SUPPORT_INDEXED_ISA:通过一些资料查询,它是用于手表的;
    • nonpointer:它为1时,表示使用了优化的isa指针,包含了引用计数,析构状态等;
    • has_assoc: 是否包含关联对象,如果没有,会更快的释放内存;
    • has_cxx_dtor:是否包含析构函数,如果没有,会更快的释放内存;
    • shiftcls:类的指针;
    • magic:用于判断是否完成初始化;
    • weakly_referenced:对象是否指向一个弱引用对象,没有弱引用对象可以更快的被释放;
    • deallocating:对象是否正在销毁;
    • has_sidetable_rc:对象的引用计数太大了,存不下;
    • extra_rc:引用计数,但是比retaincount小1;

    用一张霜神的图片,可以更容易看清楚:


    isa_内存布局

    x86_64架构上,可以看到类的指针用了47位来表示,因为类的指针要按照8字节对齐,所以后3位都是0,而为了减小内存的消耗,那3位可以表示为别的值,所以类指针真正有效的就只有44位。

    inline Class  objc_object::ISA() 
    {
    #if SUPPORT_INDEXED_ISA
        if (isa.nonpointer) {
            uintptr_t slot = isa.indexcls;
            return classForIndex((unsigned)slot);
        }
        return (Class)isa.bits;
    #else
        return (Class)(isa.bits & ISA_MASK);
    #endif
    }
    

    通过isa.bits & ISA_MASK进行位运算,只取其中[3 - 47],然后返回Class

    inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
    { 
        if (!nonpointer) {
            isa.cls = cls;
        } else {
            isa_t newisa(0);
            newisa.bits = ISA_MAGIC_VALUE;
            // isa.magic is part of ISA_MAGIC_VALUE
            // isa.nonpointer is part of ISA_MAGIC_VALUE
            newisa.has_cxx_dtor = hasCxxDtor;
            newisa.shiftcls = (uintptr_t)cls >> 3;
            
            isa = newisa;
        }
    }
    

    根据上面的图片,把ISA_MAGIC_VALUE的值放入64位空间里面,可以知道是给nonpointermagic进行了赋值,has_cxx_dtorshiftcls一样的操作。

    cache_t

    cache_t cache从字面的是缓存的意思,肯定是优化性能的,先看下它的结构:

    struct cache_t {
        struct bucket_t *_buckets;
        mask_t _mask;
        mask_t _occupied;
    }
    
    struct bucket_t {
    private:
        cache_key_t _key;
        IMP _imp;
    }
    
    • _buckets是一个散列表,用来存储方法缓存。bucket_t是一个keyIMP的结构体。
    • _mask分配bucket的总数。
    • _occupied表示目前占用缓存bucket的个数。

    Cache的作用主要是优化方法调用的性能,当对象收到消息后,会优先去Cache中查找,如果Cache中没有才会去类的方法列表找。如果没有Cache,我们每次调用方法都去类的方法找,会严重影响性能。

    class_data_bits_t

    一个类的方法,属性和遵循的协议都在class_data_bits_t里面。我们先看下代码:

    struct class_data_bits_t {
        // Values are the FAST_ flags above.
        uintptr_t bits;
        class_rw_t* data() {
            return (class_rw_t *)(bits & FAST_DATA_MASK);
        }
    }
    

    data()方法来返回class_rw_t *指针,里面存储的是类的信息,它跟objc_object::ISA()方法是一样的。

    class_rw_t和class_ro_t

    通过名字都能看出来class_rw_t是可读写的,class_ro_t是仅仅可读的,他们里面存储的都是类的相关信息,属性、方法还有遵循的协议等。

    struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        uint32_t version;
    
        const class_ro_t *ro;
    
        method_array_t methods;
        property_array_t properties;
        protocol_array_t protocols;
    
        Class firstSubclass;
        Class nextSiblingClass;
    
        char *demangledName;
    }
    

    其中有一个指向常量的指针ro,它存储了当前类在编译期就已经确定的属性、方法以及遵循的协议。

    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize;
    #ifdef __LP64__
        uint32_t reserved;
    #endif
    
        const uint8_t * ivarLayout;
        
        const char * name;
        method_list_t * baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars;
    
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
    
        method_list_t *baseMethods() const {
            return baseMethodList;
        }
    };
    

    这个结构跟通过clang -rewrite-objc之后产出的类结构完全一样:

    struct _class_ro_t {
        unsigned int flags;
        unsigned int instanceStart;
        unsigned int instanceSize;
        unsigned int reserved;
        const unsigned char *ivarLayout;
        const char *name;
        const struct _method_list_t *baseMethods;
        const struct _objc_protocol_list *baseProtocols;
        const struct _ivar_list_t *ivars;
        const unsigned char *weakIvarLayout;
        const struct _prop_list_t *properties;
    };
    
    struct _class_t {
        struct _class_t *isa;
        struct _class_t *superclass;
        void *cache;
        void *vtable;
        struct _class_ro_t *ro;
    };
    

    这样的类结构跟这里的类结构还有一点不一样就是class_rw_t,那它是怎么调整的呢?什么时候操作呢?还有运行期类的信息什么时候赋值呢?

    map_images_nolock

    通过调式,发现Runtime的入口是 objc-os.mm 里面的函数 _objc_init。在这个函数里面,动态库注册了3个函数回调map_2_imagesload_imagesunmap_image

    void _objc_init(void)
    └──const char *map_2_images(...)
        └──const char *map_images_nolock(...)
            └──void _read_images(header_info **hList, uint32_t hCount)
    
    void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                      const struct mach_header * const mhdrs[])
    {
        static bool firstTime = YES;
        header_info *hList[mhCount];
        uint32_t hCount;
        size_t selrefCount = 0;
        if (firstTime) {
            preopt_init();
        }
        int totalClasses = 0;
        int unoptimizedTotalClasses = 0;
        {
            uint32_t i = mhCount;
            while (i--) {
                const headerType *mhdr = (const headerType *)mhdrs[I];
                // 将mhdr通过appendHeader函数添加到链表中 由FirstHeader,LastHeader和HeaderCount维护
                auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
                if (!hi) {
                    // no objc data in this entry
                    continue;
                }
                
                if (mhdr->filetype == MH_EXECUTE) {
                    // Size some data structures based on main executable's size
    #if __OBJC2__
                    size_t count;
                    _getObjc2SelectorRefs(hi, &count);
                    selrefCount += count;
                    //Fix up old objc_msgSend_fixup call sites 基本上都为0
                    _getObjc2MessageRefs(hi, &count);
                    selrefCount += count;
    #else
                    _getObjcSelectorRefs(hi, &selrefCount);
    #endif
                }
                
                hList[hCount++] = hi;
            }
        }
    
        if (firstTime) {
            //将被引用的sel数量存到静态变量SelrefCount中;
            //注册一些sel到namedSelectors中
            sel_init(selrefCount);
            //初始化AutoreleasePoolPage用于autorelease机制
            // 初始化SideTable用于__weak变量引用管理
            arr_init();
        }
    
        if (hCount > 0) {
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
        firstTime = NO;
    }
    

    这个函数主要就是通过mhdrs生成hList,然后传递给_read_images函数。

    _read_images

    这个方法是处理类的相关信息的,有些长,一点一点来分析。

    类信息

    for (EACH_HEADER) {
            if (! mustReadClasses(hi)) {
                // Image is sufficiently optimized that we need not call readClass()
                continue;
            }
    
            bool headerIsBundle = hi->isBundle();
            bool headerIsPreoptimized = hi->isPreoptimized();
    
            classref_t *classlist = _getObjc2ClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = (Class)classlist[I];
                Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
    
                ...
            }
        }
    
    bool mustReadClasses(header_info *hi)
    {
        const char *reason;
    
        // If the image is not preoptimized then we must read classes.
        if (!hi->isPreoptimized()) {
            reason = nil; // Don't log this one because it is noisy.
            goto readthem;
        }
    
        assert(!hi->isBundle());  // no MH_BUNDLE in shared cache
    
        // If the image may have missing weak superclasses then we must read classes
        if (!noMissingWeakSuperclasses()) {
            reason = "the image may contain classes with missing weak superclasses";
            goto readthem;
        }
    
        // If there are unresolved future classes then we must read classes.
        if (haveFutureNamedClasses()) {
            reason = "there are unresolved future classes pending";
            goto readthem;
        }
    
        // readClass() does not need to do anything.
        return NO;
    
     readthem:
        return YES;
    }
    
    bool header_info::isPreoptimized() const
    {
        // preoptimization disabled for some reason
        if (!preoptimized) return NO;
    
        // image not from shared cache, or not fixed inside shared cache
        if (!info()->optimizedByDyld()) return NO;
    
        return YES;
    }
    

    map_images_nolock函数中调用了preopt_init();,通过调式,它会把preoptimized置为NO,所以isPreoptimized函数也是一直返回NO的,因此mustReadClasses会返回YES

    _getObjc2ClassList函数会调用getsectiondata来返回所有的类列表,这是读取二进制文件也就是mach-o得到的,具体的之前专门文章介绍。

    Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
    {
        const char *mangledName = cls->mangledName();
       ...省略
       addNamedClass(cls, mangledName, replacing);
        // for future reference: shared cache never contains MH_BUNDLEs
        if (headerIsBundle) {
            cls->data()->flags |= RO_FROM_BUNDLE;
            cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
        }
        return cls;
    }
    
    static void addNamedClass(Class cls, const char *name, Class replacing = nil)
    {
        runtimeLock.assertWriting();
        Class old;
        if ((old = getClass(name))  &&  old != replacing) {
            inform_duplicate(name, old, cls);
            // getNonMetaClass uses name lookups. Classes not found by name 
            // lookup must be in the secondary meta->nonmeta table.
            addNonMetaClass(cls);
        } else {
            NXMapInsert(gdb_objc_realized_classes, name, cls);
        }
    }
    

    readClass函数主要就是调用addNamedClass函数,通过NXMapInsert把类插入进去,把类跟类名做一个关联。

    方法信息

    static size_t UnfixedSelectors;
        sel_lock();
        for (EACH_HEADER) {
            if (hi->isPreoptimized()) continue;
    
            bool isBundle = hi->isBundle();
            SEL *sels = _getObjc2SelectorRefs(hi, &count);
            UnfixedSelectors += count;
            for (i = 0; i < count; i++) {
                const char *name = sel_cname(sels[i]);
                sels[i] = sel_registerNameNoLock(name, isBundle);
            }
        }
        sel_unlock();
    

    这个就比较简单了,就是把方法名插入到namedSelectors表里面。

    协议信息

    struct protocol_t : objc_object {
        const char *mangledName;
        ...
    }
    // Discover protocols. Fix up protocol refs.
        for (EACH_HEADER) {
            extern objc_class OBJC_CLASS_$_Protocol;
            Class cls = (Class)&OBJC_CLASS_$_Protocol;
            assert(cls);
            NXMapTable *protocol_map = protocols();
            bool isPreoptimized = hi->isPreoptimized();
            bool isBundle = hi->isBundle();
    
            protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
            for (i = 0; i < count; i++) {
                readProtocol(protolist[i], cls, protocol_map, 
                             isPreoptimized, isBundle);
            }
        }
    
        static void readProtocol(protocol_t *newproto, Class protocol_class,
                 NXMapTable *protocol_map, 
                 bool headerIsPreoptimized, bool headerIsBundle)
        {
                auto insertFn = headerIsBundle ? NXMapKeyCopyingInsert : NXMapInsert;
                ...
                newproto->initIsa(protocol_class);  // fixme pinned
                insertFn(protocol_map, newproto->mangledName, newproto);
                ...
        }
    
        // Fix up @protocol references
        // Preoptimized images may have the right 
        // answer already but we don't know for sure.
        for (EACH_HEADER) {
            protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
            for (i = 0; i < count; i++) {
                remapProtocolRef(&protolist[I]);
            }
        }
        
    static void remapProtocolRef(protocol_t **protoref)
    {
        runtimeLock.assertLocked();
    
        protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
        if (*protoref != newproto) {
            *protoref = newproto;
            UnfixedProtocolReferences++;
        }
    }
    

    通过查看protocol_t结构,发现它本质上也是一个对象,它的isa就是(Class)&OBJC_CLASS_$_Protocol,然后把协议都插入到protocol_map表里。

    通过*protoref的名字,如果能在protocol_map找到,而且地址不一样,就进行替换。

    non-lazy classes

    // Realize non-lazy classes (for +load methods and static instances)
        for (EACH_HEADER) {
            classref_t *classlist = 
                _getObjc2NonlazyClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
                if (!cls) continue;
                realizeClass(cls);
            }
        }
    

    _getObjc2NonlazyClassList是获取有重写+load方法的类,对应的是_OBJC_LABEL_NONLAZY_CLASS_$[]。这个方法的重点是realizeClass

    realizeClass

    static Class realizeClass(Class cls)
    {
        runtimeLock.assertWriting();
        const class_ro_t *ro;
        class_rw_t *rw;
        Class supercls;
        Class metacls;
        bool isMeta;
    
        if (!cls) return nil;
        if (cls->isRealized()) return cls;
    
        ro = (const class_ro_t *)cls->data();
        if (ro->flags & RO_FUTURE) {
            // This was a future class. rw data is already allocated.
            rw = cls->data();
            ro = cls->data()->ro;
            cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
        } else {
            // Normal class. Allocate writeable class data.
            rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
            rw->ro = ro;
            rw->flags = RW_REALIZED|RW_REALIZING;
            cls->setData(rw);
        }
    
        isMeta = ro->flags & RO_META;
    
        rw->version = isMeta ? 7 : 0;  // old runtime went up to 6
        // Choose an index for this class.
        // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
        cls->chooseClassArrayIndex();
        // Realize superclass and metaclass, if they aren't already.
        // This needs to be done after RW_REALIZED is set above, for root classes.
        // This needs to be done after class index is chosen, for root metaclasses.
        supercls = realizeClass(remapClass(cls->superclass));
        metacls = realizeClass(remapClass(cls->ISA()));
    
    ...省略
    
        // Update superclass and metaclass in case of remapping
        cls->superclass = supercls;
        cls->initClassIsa(metacls);
    
        // Reconcile instance variable offsets / layout.
        // This may reallocate class_ro_t, updating our ro variable.
        if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);
    
        // Set fastInstanceSize if it wasn't set already.
        cls->setInstanceSize(ro->instanceSize);
    
        // Copy some flags from ro to rw
        if (ro->flags & RO_HAS_CXX_STRUCTORS) {
            cls->setHasCxxDtor();
            if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
                cls->setHasCxxCtor();
            }
        }
    
        // Connect this class to its superclass's subclass lists
        if (supercls) {
            addSubclass(supercls, cls);
        } else {
            addRootClass(cls);
        }
    
        // Attach categories
        methodizeClass(cls);
        return cls;
    }
    

    ro = (const class_ro_t *)cls->data();,从这行代码可以看出从二进制读取到的class_ro_t结构,而不是class_rw_t

    rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
    rw->ro = ro;
    rw->flags = RW_REALIZED|RW_REALIZING;
    cls->setData(rw);
    
    // class_t->data is class_rw_t, not class_ro_t
    #define RW_REALIZED           (1<<31)
    
    // class has started realizing but not yet completed it
    #define RW_REALIZING          (1<<19)
    

    这几行代码就是给rw结构进行赋值,并对flag进行改变,表示开始realizing,但还没完成。

    if (!cls) return nil;
    if (cls->isRealized()) return cls;
        
    supercls = realizeClass(remapClass(cls->superclass));
    metacls = realizeClass(remapClass(cls->ISA()));
    

    通过这4行代码,递归对类进行realize,从这也能看出类的superclassisa结构,跟这个图进行认证。

    class_isa_image

    cls->initClassIsa(metacls);这行代码可以说明类的isa就是metacls

    methodizeClass这个方法主要就是对rw的方法、协议、属性进行赋值。这里有一点比较奇怪的是为啥就仅仅对重写+load方法的类进行realizeClass,而不是所有的类?我就全局搜索都有那些地方调用这个方法,发现有好多地方会调用,如lookUpImpOrForwardlook_up_class等,他们都会进行判断,如果类没有isRealized,就会调用这个方法。这样做的目的可能是为了性能考虑,让启程尽快启动。

    分类

    for (EACH_HEADER) {
            category_t **catlist = 
                _getObjc2CategoryList(hi, &count);
            bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    
            for (i = 0; i < count; i++) {
                category_t *cat = catlist[I];
                Class cls = remapClass(cat->cls);
                ...
                // Process this category. 
                // First, register the category with its target class. 
                // Then, rebuild the class's method lists (etc) if 
                // the class is realized. 
                bool classExists = NO;
                if (cat->instanceMethods ||  cat->protocols  
                    ||  cat->instanceProperties) 
                {
                    addUnattachedCategoryForClass(cat, cls, hi);
                    if (cls->isRealized()) {
                        remethodizeClass(cls);
                        classExists = YES;
                    }
                }
    
                if (cat->classMethods  ||  cat->protocols  
                    ||  (hasClassProperties && cat->_classProperties)) 
                {
                    addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                    if (cls->ISA()->isRealized()) {
                        remethodizeClass(cls->ISA());
                    }
                }
            }
        }
    
    struct category_t {
        const char *name;
        classref_t cls;
        struct method_list_t *instanceMethods;
        struct method_list_t *classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    
        method_list_t *methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
    
        property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    };
    

    addUnattachedCategoryForClass方法把分类的相关信息存到category_map表里面。接下来如果类已经isRealized过了,就调用remethodizeClass方法,它首先在分类表里根据cls把相关信息删除,再调用attachCategories方法。这个方法就是把分类的方法、属性、协议加入到类里面。

    static void  attachCategories(Class cls, category_list *cats, bool flush_caches)
    {
        if (!cats) return;
        if (PrintReplacedMethods) printReplacements(cls, cats);
    
        bool isMeta = cls->isMetaClass();
    
        // fixme rearrange to remove these intermediate allocations
        method_list_t **mlists = (method_list_t **)
            malloc(cats->count * sizeof(*mlists));
        property_list_t **proplists = (property_list_t **)
            malloc(cats->count * sizeof(*proplists));
        protocol_list_t **protolists = (protocol_list_t **)
            malloc(cats->count * sizeof(*protolists));
    
        // Count backwards through cats to get newest categories first
        int mcount = 0;
        int propcount = 0;
        int protocount = 0;
        int i = cats->count;
        bool fromBundle = NO;
        while (i--) {
            auto& entry = cats->list[I];
    
            method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
            if (mlist) {
                mlists[mcount++] = mlist;
                fromBundle |= entry.hi->isBundle();
            }
    
            property_list_t *proplist = 
                entry.cat->propertiesForMeta(isMeta, entry.hi);
            if (proplist) {
                proplists[propcount++] = proplist;
            }
    
            protocol_list_t *protolist = entry.cat->protocols;
            if (protolist) {
                protolists[protocount++] = protolist;
            }
        }
    
        auto rw = cls->data();
    
        prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
        rw->methods.attachLists(mlists, mcount);
        free(mlists);
        if (flush_caches  &&  mcount > 0) flushCaches(cls);
    
        rw->properties.attachLists(proplists, propcount);
        free(proplists);
    
        rw->protocols.attachLists(protolists, protocount);
        free(protolists);
    }
    

    这个方法比较简单,就是从cats获取所有的分类信息,然后对rw进行赋值。

    这里面第一个回调函数map_images就没了,接下来是load_images函数,它主要是操作+load方法。

    load_images

    void load_images(const char *path __unused, const struct mach_header *mh)
    {
        // Return without taking locks if there are no +load methods here.
        if (!hasLoadMethods((const headerType *)mh)) return;
        recursive_mutex_locker_t lock(loadMethodLock);
        // Discover load methods
        {
            rwlock_writer_t lock2(runtimeLock);
            prepare_load_methods((const headerType *)mh);
        }
        // Call +load methods (without runtimeLock - re-entrant)
        call_load_methods();
    }
    

    通过prepare_load_methods函数找到所有重写+load方法的类和分类,然后通过call_load_methods调用他们。

    void prepare_load_methods(const headerType *mhdr)
    {
        size_t count, I;
    
        runtimeLock.assertWriting();
    
        classref_t *classlist = 
            _getObjc2NonlazyClassList(mhdr, &count);
        for (i = 0; i < count; i++) {
            schedule_class_load(remapClass(classlist[i]));
        }
    
        category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = categorylist[I];
            Class cls = remapClass(cat->cls);
            if (!cls) continue;  // category for ignored weak-linked class
            realizeClass(cls);
            assert(cls->ISA()->isRealized());
            add_category_to_loadable_list(cat);
        }
    }
    

    _getObjc2NonlazyClassList前面已经讲过,_getObjc2NonlazyCategoryList它获取的是重写+load的所有分类,它对应的是_OBJC_LABEL_NONLAZY_CATEGORY_$[]

    // class +load has been called
    #define RW_LOADED             (1<<23)
    
    static void schedule_class_load(Class cls)
    {
        if (!cls) return;
        if (cls->data()->flags & RW_LOADED) return;
        schedule_class_load(cls->superclass);
        add_class_to_loadable_list(cls);
        cls->setInfo(RW_LOADED); 
    }
    
    static struct loadable_category *loadable_categories = nil;
    static int loadable_categories_used = 0;
    static int loadable_categories_allocated = 0;
    
    struct loadable_category {
        Category cat;  // may be nil
        IMP method;
    };
    
    void add_class_to_loadable_list(Class cls)
    {
        IMP method;
        loadMethodLock.assertLocked();
        method = cls->getLoadMethod();
        if (!method) return;  // Don't bother if cls has no +load method
        if (loadable_classes_used == loadable_classes_allocated) {
            loadable_classes_allocated = loadable_classes_allocated*2 + 16;
            loadable_classes = (struct loadable_class *)
                realloc(loadable_classes,
                                  loadable_classes_allocated *
                                  sizeof(struct loadable_class));
        }
        
        loadable_classes[loadable_classes_used].cls = cls;
        loadable_classes[loadable_classes_used].method = method;
        loadable_classes_used++;
    }
    
    IMP objc_class::getLoadMethod()
    {
        runtimeLock.assertLocked();
    
        const method_list_t *mlist;
        mlist = ISA()->data()->ro->baseMethods();
        if (mlist) {
            for (const auto& meth : *mlist) {
                const char *name = sel_cname(meth.name);
                if (0 == strcmp(name, "load")) {
                    return meth.imp;
                }
            }
        }
        return nil;
    }
    

    schedule_class_load(cls->superclass);这行代码递归调用直至到NSObject,因为NSObject的父类为nil,然后通过add_class_to_loadable_list方法把有+load方法加入到loadable_classes,最后把当前类的flag置为+load已经调用的状态。从这也可以看到是先从父类的+load开始算起的。

    void add_category_to_loadable_list(Category cat)
    {
        IMP method;
        loadMethodLock.assertLocked();
        method = _category_getLoadMethod(cat);
    
        // Don't bother if cat has no +load method
        if (!method) return;
        
        if (loadable_categories_used == loadable_categories_allocated) {
            loadable_categories_allocated = loadable_categories_allocated*2 + 16;
            loadable_categories = (struct loadable_category *)
                realloc(loadable_categories,
                                  loadable_categories_allocated *
                                  sizeof(struct loadable_category));
        }
    
        loadable_categories[loadable_categories_used].cat = cat;
        loadable_categories[loadable_categories_used].method = method;
        loadable_categories_used++;
    }
    

    分类的+load存取是直接从分类的方法找到有+load的,然后放到loadable_categories

    call_load_methods

    void call_load_methods(void)
    {
        static bool loading = NO;
        bool more_categories;
    
        loadMethodLock.assertLocked();
        if (loading) return;
        loading = YES;
    
        void *pool = objc_autoreleasePoolPush();
        do {
            // 1. Repeatedly call class +loads until there aren't any more
            while (loadable_classes_used > 0) {
                call_class_loads();
            }
    
            // 2. Call category +loads ONCE
            more_categories = call_category_loads();
    
            // 3. Run more +loads if there are classes OR more untried categories
        } while (loadable_classes_used > 0  ||  more_categories);
    
        objc_autoreleasePoolPop(pool);
        loading = NO;
    }
    
    static void call_class_loads(void)
    {
        int I;
        
        // Detach current loadable list.
        struct loadable_class *classes = loadable_classes;
        int used = loadable_classes_used;
        loadable_classes = nil;
        loadable_classes_allocated = 0;
        loadable_classes_used = 0;
        
        // Call all +loads for the detached list.
        for (i = 0; i < used; i++) {
            Class cls = classes[i].cls;
            load_method_t load_method = (load_method_t)classes[i].method;
            if (!cls) continue; 
            (*load_method)(cls, SEL_load);
        }
        if (classes) free(classes);
    }
    

    调用+load方法也是先调用类的,再调用分类的,类的调用就是从loadable_classes获取所有的类的+load实现,然后直接调用。

    * call_load_methods
    * Call all pending class and category +load methods.
    * Class +load methods are called superclass-first. 
    * Category +load methods are not called until after the parent class's +load.
    

    这段注释也能很好的诠释这些。

    static bool call_category_loads(void)
    {
        int i, shift;
        bool new_categories_added = NO;
        
        // Detach current loadable list.
        struct loadable_category *cats = loadable_categories;
        int used = loadable_categories_used;
        int allocated = loadable_categories_allocated;
        loadable_categories = nil;
        loadable_categories_allocated = 0;
        loadable_categories_used = 0;
    
        // Call all +loads for the detached list.
        for (i = 0; i < used; i++) {
            Category cat = cats[i].cat;
            load_method_t load_method = (load_method_t)cats[i].method;
            Class cls;
            if (!cat) continue;
    
            cls = _category_getClass(cat);
            if (cls  &&  cls->isLoadable()) {
                (*load_method)(cls, SEL_load);
                cats[i].cat = nil;
            }
        }
    
        // Compact detached list (order-preserving)
        shift = 0;
        for (i = 0; i < used; i++) {
            if (cats[i].cat) {
                cats[i-shift] = cats[I];
            } else {
                shift++;
            }
        }
        used -= shift;
    
        // Copy any new +load candidates from the new list to the detached list.
        new_categories_added = (loadable_categories_used > 0);
        for (i = 0; i < loadable_categories_used; i++) {
            if (used == allocated) {
                allocated = allocated*2 + 16;
                cats = (struct loadable_category *)
                    realloc(cats, allocated *
                                      sizeof(struct loadable_category));
            }
            cats[used++] = loadable_categories[I];
        }
    
        // Destroy the new list.
        if (loadable_categories) free(loadable_categories);
    
        // Reattach the (now augmented) detached list. 
        // But if there's nothing left to load, destroy the list.
        if (used) {
            loadable_categories = cats;
            loadable_categories_used = used;
            loadable_categories_allocated = allocated;
        } else {
            if (cats) free(cats);
            loadable_categories = nil;
            loadable_categories_used = 0;
            loadable_categories_allocated = 0;
        }
    
        return new_categories_added;
    }
    

    这个方法我是有点疑惑的,不太明白又加层机制是为什么?首先cls->isLoadable()是一直返回yes的,cls不存在的可能性我认为不存在(这点有待探讨)。Re-entrant可重入问题,在call_load_methods里面已经解决了。

    // Re-entrant calls do nothing; the outermost call will finish the job.
        if (loading) return;
        loading = YES;
    

    至于在+load方法里面动态生成一个分类,那为啥类的时候不考虑那种情况呢,再说loadable_categories是在prepare_load_methods方法里面确定好的,没有那个地方再对它进行修改。综合几种情况下来,还是不太明白为啥那样做的原因。

    引用计数

    这里简单的说下引用计数的原理,引用计数跟objc_storeStrong这个函数紧密相连,引用计数的增加减少都是通过它,这个函数是编译阶段由编译器在合适的位置自己增加的,所以才叫自动引用计数。还有一种情况是直接调用objc_retain增加引用计数的,但是减少的话都是通过objc_storeStrong。(这段话可能不太准确,但是我测试好几种情况都是这样的,但是不管还有那种情况,最后肯定都是调用retainrelease来进行增加减少引用计数的)

    void objc_storeStrong(id *location, id obj)
    {
        id prev = *location;
        if (obj == prev) {
            return;
        }
        objc_retain(obj);
        *location = obj;
        objc_release(prev);
    }
    

    下面看2个例子比较容易明白,其中第2个例子就是直接调用objc_retain来增加引用计数的。

    #1:
    {
        NSObject *obj1 = [[NSObject alloc]init];
        NSObject *obj2 = [[NSObject alloc]init];
        obj2 = obj1;
    }
    
    #2:
    {
        NSObject *obj1 = [[NSObject alloc]init];
        NSObject *obj2 = obj1;
    }
    

    当出括号时,会调用2次objc_storeStrong,当调用第2个时,会调用dealloc

    objc_object::rootRetain(bool tryRetain, bool handleOverflow)
    {
        if (isTaggedPointer()) return (id)this;
    
        bool sideTableLocked = false;
        bool transcribeToSideTable = false;
    
        isa_t oldisa;
        isa_t newisa;
    
        do {
            transcribeToSideTable = false;
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
            if (slowpath(!newisa.nonpointer)) {
                ClearExclusive(&isa.bits);
                if (!tryRetain && sideTableLocked) sidetable_unlock();
                if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
                else return sidetable_retain();
            }
            // don't check newisa.fast_rr; we already called any RR overrides
            if (slowpath(tryRetain && newisa.deallocating)) {
                ClearExclusive(&isa.bits);
                if (!tryRetain && sideTableLocked) sidetable_unlock();
                return nil;
            }
            uintptr_t carry;
            newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++
    
            if (slowpath(carry)) {
                // newisa.extra_rc++ overflowed
                if (!handleOverflow) {
                    ClearExclusive(&isa.bits);
                    return rootRetain_overflow(tryRetain);
                }
                // Leave half of the retain counts inline and 
                // prepare to copy the other half to the side table.
                if (!tryRetain && !sideTableLocked) sidetable_lock();
                sideTableLocked = true;
                transcribeToSideTable = true;
                newisa.extra_rc = RC_HALF;
                newisa.has_sidetable_rc = true;
            }
        } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));
    
        if (slowpath(transcribeToSideTable)) {
            // Copy the other half of the retain counts to the side table.
            sidetable_addExtraRC_nolock(RC_HALF);
        }
    
        if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
        return (id)this;
    }
    

    最主要的是newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry); // extra_rc++这行代码,它是增加引用计数的,上面讲述isa时,知道最后的8位是存引用计数的,如果引用计数太大,8位不够存时(超过255),就用sideTable表结构,这一块就不讲了,因为感觉用不到,255已经够大了。

    static ALWAYS_INLINE uintptr_t 
    addc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout)
    {
        return __builtin_addcl(lhs, rhs, carryin, carryout);
    }
    
    static ALWAYS_INLINE uintptr_t 
    subc(uintptr_t lhs, uintptr_t rhs, uintptr_t carryin, uintptr_t *carryout)
    {
        return __builtin_subcl(lhs, rhs, carryin, carryout);
    }
    

    这2个函数是LLVM提供的,就是简单的数相加,不过如果超过最大值或者最小值时carryout就不为0了。从这也能看出来为啥实际的引用计数比extra_rc加1的原因,alloc的时候是不增加引用计数的。

    ALWAYS_INLINE bool 
    objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
    {
        if (isTaggedPointer()) return false;
    
        bool sideTableLocked = false;
    
        isa_t oldisa;
        isa_t newisa;
    
     retry:
        do {
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
            if (slowpath(!newisa.nonpointer)) {
                ClearExclusive(&isa.bits);
                if (sideTableLocked) sidetable_unlock();
                return sidetable_release(performDealloc);
            }
            // don't check newisa.fast_rr; we already called any RR overrides
            uintptr_t carry;
            newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
            if (slowpath(carry)) {
                // don't ClearExclusive()
                goto underflow;
            }
        } while (slowpath(!StoreReleaseExclusive(&isa.bits, 
                                                 oldisa.bits, newisa.bits)));
    
        if (slowpath(sideTableLocked)) sidetable_unlock();
        return false;
    
     underflow:
        // newisa.extra_rc-- underflowed: borrow from side table or deallocate
    
        // abandon newisa to undo the decrement
        newisa = oldisa;
    
        if (slowpath(newisa.has_sidetable_rc)) {
            if (!handleUnderflow) {
                ClearExclusive(&isa.bits);
                return rootRelease_underflow(performDealloc);
            }
    
            // Transfer retain count from side table to inline storage.
    
            if (!sideTableLocked) {
                ClearExclusive(&isa.bits);
                sidetable_lock();
                sideTableLocked = true;
                // Need to start over to avoid a race against 
                // the nonpointer -> raw pointer transition.
                goto retry;
            }
    
            // Try to remove some retain counts from the side table.        
            size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
    
            // To avoid races, has_sidetable_rc must remain set 
            // even if the side table count is now zero.
    
            if (borrowed > 0) {
                // Side table retain count decreased.
                // Try to add them to the inline count.
                newisa.extra_rc = borrowed - 1;  // redo the original decrement too
                bool stored = StoreReleaseExclusive(&isa.bits, 
                                                    oldisa.bits, newisa.bits);
                if (!stored) {
                    // Inline update failed. 
                    // Try it again right now. This prevents livelock on LL/SC 
                    // architectures where the side table access itself may have 
                    // dropped the reservation.
                    isa_t oldisa2 = LoadExclusive(&isa.bits);
                    isa_t newisa2 = oldisa2;
                    if (newisa2.nonpointer) {
                        uintptr_t overflow;
                        newisa2.bits = 
                            addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
                        if (!overflow) {
                            stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits, 
                                                           newisa2.bits);
                        }
                    }
                }
    
                if (!stored) {
                    // Inline update failed.
                    // Put the retains back in the side table.
                    sidetable_addExtraRC_nolock(borrowed);
                    goto retry;
                }
    
                // Decrement successful after borrowing from side table.
                // This decrement cannot be the deallocating decrement - the side 
                // table lock and has_sidetable_rc bit ensure that if everyone 
                // else tried to -release while we worked, the last one would block.
                sidetable_unlock();
                return false;
            }
            else {
                // Side table is empty after all. Fall-through to the dealloc path.
            }
        }
    
        // Really deallocate.
    
        if (slowpath(newisa.deallocating)) {
            ClearExclusive(&isa.bits);
            if (sideTableLocked) sidetable_unlock();
            return overrelease_error();
            // does not actually return
        }
        newisa.deallocating = true;
        if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
    
        if (slowpath(sideTableLocked)) sidetable_unlock();
    
        __sync_synchronize();
        if (performDealloc) {
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
        }
        return true;
    }
    

    减少引用计数后,if (slowpath(carry)) goto underflow;},直接就开始((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);

    runtime相关就讲到这里,如果有什么错误,请相互交流指正。

    相关文章

      网友评论

          本文标题:Runtime-类

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