類是個結(jié)構(gòu)體
在
objc-private.h和objc-runtime-new.h文件中,我們找到了objc_object和objc_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_object和objc_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包含了類中的大部分信息,包含了mehtod,property,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。

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

在上面打印的結(jié)構(gòu)中,發(fā)現(xiàn)了
methods、properties和protocols三個結(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中,只發(fā)現(xiàn)了屬性中的nickName,卻沒有發(fā)現(xiàn)對應(yīng)的成員變量hobby,那成員變量存儲在哪里?
發(fā)現(xiàn)只有ro是一個未知內(nèi)容,那么我們就輸出看看ro里面是什么內(nèi)容。

此時,我們發(fā)現(xiàn)
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,property,protocol。因此我們直接在ro中探索實例方法。
在ro中的baseMethodList中,發(fā)現(xiàn)了method_list_t結(jié)構(gòu),并在里面找到了方法列表,而且count = 4,為此我們逐一打印出來。

不僅查找到定義的實例方法,還發(fā)現(xiàn)了系統(tǒng)自動為屬性生成的
getter和setter方法
-
類方法
查找到了屬性,成員變量,實例方法,但是卻沒有找到定義的類方法。
既然當(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)在Class和metaClass中沒有值,而類方法(sayHappy)在Class和metaClass中有值。
為什么類方法在元類的實例方法和類方法中都有值呢?
/***********************************************************************
* 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)中就能了解問題的所在,最后取得是metaClass的sel。因此我們可以猜測其類方法存儲于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ù)屬性生成的getter和setter也存儲于此;
類方法存儲于metaClass中的ro.baseMethodList。