類的內(nèi)容

類是個結(jié)構(gòu)體

objc-private.hobjc-runtime-new.h文件中,我們找到了objc_objectobjc_class兩個結(jié)構(gòu)體。

struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    ...
}

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

typedef struct objc_class *Class;
typedef struct objc_object *id;

通過objc_objectobjc_class兩個結(jié)構(gòu)體,我們可以看到主要內(nèi)容有四個

  • Class ISA
  • Class superclass
  • cache_t cache
  • class_data_bits_t bits

ISA是類的關(guān)聯(lián)。
superclass是父類信息的地址指針。

cache_t cache

struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic<struct bucket_t *> _buckets;
    explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    ...
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    ...
#else
#error Unknown cache mask storage type.
#endif
    
#if __LP64__
    uint16_t _flags;
#endif
    uint16_t _occupied;

public:
    ...
    void insert(Class cls, SEL sel, IMP imp, id receiver);
    ...
};

struct bucket_t {
private:
    // IMP-first is better for arm64e ptrauth and no worse for arm64.
    // SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
    explicit_atomic<uintptr_t> _imp;
    explicit_atomic<SEL> _sel;
#else
    explicit_atomic<SEL> _sel;
    explicit_atomic<uintptr_t> _imp;
#endif
    ...
};

cache_t cache通過備注描述是一個指針的緩存和虛函數(shù)表;
包含了一個結(jié)構(gòu)體指針struct bucket_t *_buckets,以及_mask、_flags_occupied。
而根據(jù)struct bucket_t *_buckets中的參數(shù)_imp_sel,可以知道其緩存的是方法。

cache_t結(jié)構(gòu)中,發(fā)現(xiàn)有一個void insert(Class cls, SEL sel, IMP imp, id receiver);函數(shù),而調(diào)用這個函數(shù)的Caller則是cache_fill函數(shù)。
這里我們不深究調(diào)用棧,而是要了解一下cache存儲填充邏輯。

void cache_fill(Class cls, SEL sel, IMP imp, id receiver) {
    runtimeLock.assertLocked();

#if !DEBUG_TASK_THREADS
    // Never cache before +initialize is done
    if (cls->isInitialized()) {
        cache_t *cache = getCache(cls);
#if CONFIG_USE_CACHE_LOCK
        mutex_locker_t lock(cacheUpdateLock);
#endif
        cache->insert(cls, sel, imp, receiver);
    }
#else
    _collecting_in_critical();
#endif
}

cache_fill函數(shù)中:

  • runtimeLock.assertLocked();開啟線程鎖;
  • if(cls->isInitialized())如果類沒有進行初始化操作,則不能進行相應(yīng)的緩存操作;
  • cache->insert(cls, sel, imp, receiver);進行緩存操作。
// 對方法進行插入緩存操作
ALWAYS_INLINE
void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver) {
#if CONFIG_USE_CACHE_LOCK
    cacheUpdateLock.assertLocked();
#else
    runtimeLock.assertLocked();
#endif

    ASSERT(sel != 0 && cls->isInitialized());

    // Use the cache as-is if it is less than 3/4 full
    mask_t newOccupied = occupied() + 1;    // 當(dāng)前占有量 + 1
    unsigned oldCapacity = capacity(), capacity = oldCapacity;  //獲取當(dāng)前最大容量
    if (slowpath(isConstantEmptyCache())) {
        // Cache is read-only. Replace it.
        if (!capacity) capacity = INIT_CACHE_SIZE;    // 默認(rèn)INIT_CACHE_SIZE容量
        reallocate(oldCapacity, capacity, /* freeOld */false);
    }
    else if (fastpath(newOccupied <= capacity / 4 * 3)) {
        // Cache is less than 3/4 full. Use it as-is.
    }
    else {
        capacity = capacity ? capacity * 2 : INIT_CACHE_SIZE;
        if (capacity > MAX_CACHE_SIZE) {
            capacity = MAX_CACHE_SIZE;
        }
        reallocate(oldCapacity, capacity, true);
    }

    bucket_t *b = buckets();
    mask_t m = capacity - 1;
    mask_t begin = cache_hash(sel, m);
    mask_t i = begin;

    // Scan for the first unused slot and insert there.
    // There is guaranteed to be an empty slot because the
    // minimum size is 4 and we resized at 3/4 full.
    do {
        if (fastpath(b[i].sel() == 0)) {
            incrementOccupied();
            b[i].set<Atomic, Encoded>(sel, imp, cls);
            return;
        }
        if (b[i].sel() == sel) {
            // The entry was added to the cache by some other thread
            // before we grabbed the cacheUpdateLock.
            return;
        }
    } while (fastpath((i = cache_next(i, m)) != begin));

    cache_t::bad_cache(receiver, (SEL)sel, cls);
}
// 進行bucket的創(chuàng)建/擴容
ALWAYS_INLINE
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld) {
    bucket_t *oldBuckets = buckets();
    bucket_t *newBuckets = allocateBuckets(newCapacity);

    // Cache's old contents are not propagated. 
    // This is thought to save cache memory at the cost of extra cache fills.
    // fixme re-measure this

    ASSERT(newCapacity > 0);
    ASSERT((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);

    setBucketsAndMask(newBuckets, newCapacity - 1);
    
    if (freeOld) {
        cache_collect_free(oldBuckets, oldCapacity);
    }
}

通過上面的源碼,緩存主要有三種情況:

  • if (slowpath(isConstantEmptyCache()))
    如果是一個空的cache,那就創(chuàng)建一個新的桶子bucket_t,然后進行緩存。
  • else if (fastpath(newOccupied <= capacity / 4 * 3))
    如果存儲的容量沒有超過3/4,不進行其他操作,直接進行緩存。
  • else
    最后,如果存儲的容量已經(jīng)超出3/4,那就創(chuàng)建一個更大(原來的兩倍)的新桶子bucket_t,釋放掉舊桶子bucket_t,然后進行緩存。

Do-while函數(shù)中,
如果在bucket_t中找到空位,則通過b[i].set<Atomic, Encoded>(sel, imp, cls);bucket_t進行設(shè)值。
如果在bucket_t中找到相同的sel,則不進行操作。
最后經(jīng)過一圈的循環(huán),沒有找到符合的bucket_t或者是一個空的bucket_t,則調(diào)用bad_cache。

class_data_bits_t bits

struct class_data_bits_t {

    // Values are the FAST_ flags above.
    uintptr_t bits;
private:
    bool getBit(uintptr_t bit) {
        return bits & bit;
    }
    ...
}

根據(jù)描述中的class_rw_t *,結(jié)合源碼中的struct class_rw_t結(jié)構(gòu)體,可以看出bits包含了類中的大部分信息,包含了mehtodproperty,protocol等。

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;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif
    ...
}

既然覺得bits結(jié)構(gòu)中存在屬性,方法,協(xié)議列表等,那就需要實際去驗證一番。
為了驗證,需要先聲明一個類,包含了屬性,成員變量,實例方法,類方法。

@interface Person : NSObject {
    NSString *hobby;
}

@property (nonatomic, copy) NSString *nickName;

- (void)sayHello;
+ (void)sayHappy;

@end

@implementation Person

- (void)sayHello {
    NSLog(@"-- %s", __func__);
}

+ (void)sayHappy {
    NSLog(@"-- %s", __func__);
}

@end

通過結(jié)構(gòu)體,我們知道ISA占8字節(jié),superclass占8字節(jié),cache占16字節(jié),所以偏移32字節(jié)(0x20)后就是bits。

類的結(jié)構(gòu)

由于class_data_bits_t bits并不是對象類型,所以在lldb打印時,需要進行類型轉(zhuǎn)換,并且輸出class_rw_t結(jié)構(gòu)中的data內(nèi)容。

bits內(nèi)容

在上面打印的結(jié)構(gòu)中,發(fā)現(xiàn)了methods、propertiesprotocols三個結(jié)構(gòu),其中都包含了list_array_tt

template <typename Element, typename List>
class list_array_tt {
    struct array_t {
        uint32_t count;
        List* lists[0];

        static size_t byteSize(uint32_t count) {
            return sizeof(array_t) + count*sizeof(lists[0]);
        }
        size_t byteSize() {
            return byteSize(count);
        }
    };

 protected:
 ...
}
  • 屬性

在輸出的class_rw_t結(jié)構(gòu)中,按照字面意思,屬性應(yīng)該存儲于properties數(shù)組中,為了驗證,需要進行內(nèi)容的打印。

properties

  • 成員變量

在上面的properties中,只發(fā)現(xiàn)了屬性中的nickName,卻沒有發(fā)現(xiàn)對應(yīng)的成員變量hobby,那成員變量存儲在哪里?

發(fā)現(xiàn)只有ro是一個未知內(nèi)容,那么我們就輸出看看ro里面是什么內(nèi)容。

ro.baseProperties

此時,我們發(fā)現(xiàn)ro存儲了更多的信息,不僅有屬性,還有一個ivars。
ro.ivars

通過ivars的打印,我們發(fā)現(xiàn)了定義好的成員變量hobby,并且發(fā)現(xiàn)了數(shù)組count = 2,經(jīng)過繼續(xù)打印還發(fā)現(xiàn)了_nickName,這就印證了系統(tǒng)會根據(jù)屬性自動生成帶下劃線(_)的成員變量。

  • 實例方法

在探索屬性和成員變量的過程中,我們發(fā)現(xiàn)在class_rw_t中的ro不僅包含了成員變量ivar,也一樣包含了mehtod,propertyprotocol。因此我們直接在ro中探索實例方法。
ro中的baseMethodList中,發(fā)現(xiàn)了method_list_t結(jié)構(gòu),并在里面找到了方法列表,而且count = 4,為此我們逐一打印出來。

實例方法

不僅查找到定義的實例方法,還發(fā)現(xiàn)了系統(tǒng)自動為屬性生成的gettersetter方法

  • 類方法

查找到了屬性,成員變量,實例方法,但是卻沒有找到定義的類方法。
既然當(dāng)前類中找不到類方法,我們嘗試的在相關(guān)聯(lián)的類中查找。

void testInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"class_getInstanceMethod");
    NSLog(@"sayHello - %p-%p", method1, method2);
    NSLog(@"sayHappy - %p-%p", method3, method4);
}

void testClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    NSLog(@"class_getClassMethod");
    NSLog(@"sayHello - %p-%p" ,method1, method2);
    NSLog(@"sayHappy - %p-%p" ,method3, method4);
}
類方法
  • class_getInstanceMethod中,
    實例方法(sayHello)在Class中有值,而類方法(sayHappy)在metaClass中有值。
  • class_getClassMethod中,
    實例方法(sayHello)在ClassmetaClass中沒有值,而類方法(sayHappy)在ClassmetaClass中有值。

為什么類方法在元類的實例方法和類方法中都有值呢?

/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel) {
    if (!cls  ||  !sel) return nil;
    return class_getInstanceMethod(cls->getMeta(), sel);
}

struct objc_class : objc_object {
  ...
// NOT identical to this->ISA when this is a metaclass
    Class getMeta() {
        if (isMetaClass()) return (Class)this;
        else return this->ISA();
    }
  ...
}

class_getClassMethod函數(shù)的實現(xiàn)中就能了解問題的所在,最后取得是metaClasssel。因此我們可以猜測其類方法存儲于metaClass中。

類方法存在于metaClass

經(jīng)過lldb的打印,也證實了類方法存在于對應(yīng)的metaClass

最后

  • 類是一個繼承于objc_object的結(jié)構(gòu)體。
  • 類結(jié)構(gòu)中cache_t cache顧名思義就是緩存,緩存的是調(diào)用過的方法。
  • 類結(jié)構(gòu)中class_data_bits_t bits包含的屬性,成員變量,實例方法,類方法,其中:
    屬性存儲于ro.baseProperties;
    成員變量存儲于ro.ivars,且系統(tǒng)根據(jù)屬性生成的成員變量也存儲于此;
    實例方法存儲于ro.baseMethodList,且系統(tǒng)根據(jù)屬性生成的gettersetter也存儲于此;
    類方法存儲于metaClass中的ro.baseMethodList
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容