類結構中的class_rw_t與class_ro_t

在OC的類結構中,存在這樣的結構:

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();
    }
    ...
    ...
}

類結構中包含了class_data_bits_t結構,這個結構實際上上是由一個指針(class_rw_t *)和一些關於類初始化狀態的的標識組成.可以通過

#if !__LP64__
    ...
// data pointer
#define FAST_DATA_MASK        0xfffffffcUL
...
#elif 1
...
#define FAST_DATA_MASK          0x00007ffffffffff8UL
...
#else
...
#define FAST_DATA_MASK          0x00007ffffffffff8UL
...
#endif
class_rw_t* data() {
    return (class_rw_t *)(bits & FAST_DATA_MASK);
    //FAST_DATA_MASK的定義跟ISA_MASK很相近
}

來獲取類結構中的class_rw_t指針.而class_rw_t結構中又包含了一個const class_ro_t *類型的指針實現,那麼class_rw_t和class_ro_t這兩個看起來只有一個字符差距的結構有什麼不一樣呢?

成員變量的不同

包含基本成員變量的不同

struct class_ro_t {
    uint32_t flags; // class_rw_t也有
    uint32_t instanceStart; //class_rw_t沒有
    uint32_t instanceSize;//class_rw_t沒有
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;//與class_rw_t中的methods對應
    protocol_list_t * baseProtocols;//與class_rw_t中的protocols對應
    const ivar_list_t * ivars; //class_rw_t沒有

    const uint8_t * weakIvarLayout;//class_rw_t沒有
    property_list_t *baseProperties; //與class_rw_t中的properties對應
    /*
    class_rw_t多出了以下成員變量
            Class firstSubclass;
            Class nextSiblingClass;
            char *demangledName;

        */


    ...
    ...
}

對比發現,class_ro_t結構中與class_rw_t對應的的主要成員變量都使用了base做區分,說明class_ro_t的結構更加貼近類本身的結構,class_rw_t像是類拓展出來的.

可變性不同

struct class_rw_t {
    ...

    const class_ro_t *ro;

    ...

}

在關於類的實現中,幾乎所有引用到class_ro_t變量的地方都是使用了const關鍵字做修飾,更像是一個靜態不願意被外界修改的屬性;而引用到class_rw_t變量就沒有這樣的限制.

作用不同

使用objc源碼進行調試,在main中創建對象

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj = [[NSObject alloc] init];
    }
}

並在setData處進行斷點:

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();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData); //在此處下斷點
    }
    ...
    ...
}

然後會發現如下調用:

發現在_read_images之後就調用了realizeClassWithoutSwift函數:

    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);
    }

而此時ro已經是一個實例化之後的class_ro_t *指針,所以class_ro_t *是在類編譯時就已經完成初始化賦值且不可被改變的.而class_rw_t結構則是可以通過方法進行修改.例如:

static SEL *
addMethods(Class cls, const SEL *names, const IMP *imps, const char **types,
           uint32_t count, bool replace, uint32_t *outFailedCount)
{
    runtimeLock.assertLocked();
    
    assert(names);
    assert(imps);
    assert(types);
    assert(cls->isRealized());
    
    ...
    ...
    
    if (newlist->count > 0) {
        // fixme resize newlist because it may have been over-allocated above.
        // Note that realloc() alone doesn't work due to ptrauth.
        
        method_t::SortBySELAddress sorter;
        std::stable_sort(newlist->begin(), newlist->end(), sorter);
        
        prepareMethodLists(cls, &newlist, 1, NO, NO);
        cls->data()->methods.attachLists(&newlist, 1);
        flushCaches(cls);
    } else {
        // Attaching the method list to the class consumes it. If we don't
        // do that, we have to free the memory ourselves.
        free(newlist);
    }
    
    if (outFailedCount) *outFailedCount = failedCount;
    
    return failedNames;
}

所以可以理解爲class_ro_t存儲的是類在編譯期就已經確定的特性,而class_rw_t則是提供在運行時進行類延展的能力.

需要注意的是,通過運行時使用

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}

來爲對象添加屬性並不是通過class_rw_t來實現的,而是通過全局維護的hashMap來實現的.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章