1.isa本質
我們知道,oc中的一切類都繼承自NSObject,直接追蹤NSObject可以發現在objc/NSObject.h文件中對于該類的定義如下
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
或者在objc源碼中找到關于類在C語言層面的定義
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
- 不難發現,每一個類的初始屬性都是一個名為isa的指針,可以看出,isa的類型為Class,而Class則代表著一切類,繼續查看Class的類型,發現Class其實是一個名為object_class的結構體指針,里面定義一些常用成員,所以初步得出isa就是一個類指針,里面存放著類的地址
typedef struct objc_class *Class;
- 繼續追蹤objc_class,發現其繼承自obj_object,實際上在舊版的代碼中,objc_class是一個單純的結構體,在new版本的代碼中才繼承自obj_object,這也符合了面向對象中,萬物皆對象的理念。在objc-runtime-new.h文件中可以看到,之后尋找objc_object的初始定義,最終在objc-private.h文件中發現了最初始的object定義以及isa的詳細結構,isa是一個聯合體位域,里面關聯了它所指向的類,也就是Class cls
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();
}
}
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
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();
}
2.isa初始化
- 在之前的alloc流程中,我們發現,在執行class_createInstance方法的時候,會調用 obj->initIsa(cls) 方法進行isa的初始化,其中cls代表當前的類,
- 當nonpointer = 0,代表當前為普通指針,那么直接通過isa.cls和類進行關聯
- 當nonpointer = 1的時候,代表是優化過的isa指針,會使用位域存放更多的信息,此時創建newisa并初始化值后賦值給isa指針
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
// 此處有一些代碼,不過不重要,暫時不會走這里
#else
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;
#endif
isa = newisa;
}
}
3.isa對類和對象的關聯
- 下面探索一下,isa是否已經和當前的類進行了關聯,先通過object_getClass方法得出結論,獲取對象的類其實就是返回當前對象的isa指針,那么就可以通過isa指針找出所屬的類
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
objc_object::getIsa()
{
// 一般都不是TaggedPointer,這是特殊指針
if (!isTaggedPointer()) return ISA();
}
objc_object::ISA()
{
assert(!isTaggedPointer());
#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
}
// 64位架構下 ISA_MASK的值為
# define ISA_MASK 0x00007ffffffffff8ULL
-
進行試驗,先通過x/4gx打印出當前對象的地址以及值,其中第一個位置必然為isa指針,那么就可以用p/x先輸出類的地址,再通過isa & ISA_MASK找出isa對應的類地址,進行比對發現兩者一致,所以得出結論isa關聯了對象和類,對象的isa指針指向該對象所屬的類,流程記錄如下圖
image.png
4.類中isa的指向
-
- 剛才驗證了isa對于類和對象的關聯,由于class繼承自object,萬物皆Object,那么繼續剛才的試驗,驗證一下類中的isa指向,廢話不多說,直接上圖
image.png
問題來了,按照剛才的步驟打印出了類的地址,但是通過isa指針尋找之后得到地址0x0000000100001108,并且打印值和之前打印類一樣均為LGPerson,但是二者的地址卻截然不同,我們知道,類在內存中只可能存在一份,對象可以存在多份,那么可以推斷,當前0x0000000100001108是一個和LGPerson名字相同但是卻不一樣的類,姑且認為它是 LG-A類
-
繼續按照步驟驗證下去,終于發現值變了,此次isa指針尋找到的類發生了變化,新找到的類名為NSObject,但是任然存在剛才的問題,這個NSObject和我們直接打印的NSObject的地址并不相同,所以暫定它為NSObject-A類
image.png
-
-
- 繼續重復步驟,發現最終NSObject-A類的isa指針一直指向自己,并且還發現有意思的是,第二個指針指向了NSObject類,其實這第二個指針就是類中的supercClass指針,所以NSObject-A類的父類其實就是NSObject類。
image.png
總結:
- 到此為止isa的本質,初始化以及isa在對象,類中的指向圖已經分析完畢,在我們分析中所提到的NSObject-A,NSObject-A其實又叫做元類,其中NSObject的元類又叫做根源類,它是一切元類的父類,所有元類的isa指針全部指向根源類,最后回歸到萬物皆對象,所以根源類的父類則是NSObject,而NSObject的父類則為nil,這里本來應該有一副很經典的isa和父類繼承路線圖,但是可能對于很多人來說理解不是很容易,所以我上了一個漢化版本
image.png