iOS 源碼分析 Class 本質(zhì),objc_class,class_rw_t,class_ro_t 分析
我們先來看下源碼內(nèi)部對(duì)clas的定義
typedef struct objc_class *Class;
可以看出來,他就是一個(gè) objc_class
指針
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();
}
我們摘出來重要的部分,可以看出來,我們的 objc_class 是繼承 objc_object
結(jié)構(gòu)體
struct objc_object {
private:
isa_t isa;
我們的 objc_object
有 isa 指針,所以我們的 class 也有 isa 指針,這就是為什么 我們可以通過我們的類對(duì)象找到我們的元類的原因,因?yàn)槲覀兊念~class 有 isa 指針,如果沒有isa指針是不行的,然后還有個(gè) superclass class 指針,只用來指向當(dāng)前類的父類的,蘋果有一張圖,畫了父類和元類的關(guān)系,指向,class 的這個(gè)結(jié)構(gòu)體,完全可以詮釋那張圖,cache_t 我之前的文章也講過,是加快查找效率的,可以看看我那篇文章,然后,下一個(gè)很重要的東西就是 bits
struct class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
bits 可以理解為一個(gè)指針,里面存放著 class_rw_t 和 class_ro_t 的地址,之前文章也有說過,蘋果為了節(jié)省空間,讓一個(gè)指針里面保存更多的信息,到時(shí)候通過按位與運(yùn)算,取出不同的值就行。
比如當(dāng)我們?nèi)〕鰜砦覀僣lass的data的時(shí)候,實(shí)際上就是取出來這個(gè)類的一些信息
class_rw_t *data() {
return bits.data();
}
可以看到,返回的是一個(gè) class_rw_t 結(jié)構(gòu)體,然后調(diào)用 bits.data();,我們看下實(shí)現(xiàn)
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
這段代碼什么意思呢?就是就是找到 bits 這個(gè)指針中 class_rw_t 這個(gè)結(jié)構(gòu)體對(duì)應(yīng)的那個(gè)data的值,通過按位與運(yùn)算,#define FAST_DATA_MASK 0x00007ffffffffff8UL
其實(shí)就是取出來,bits中第3-64位,然后就拿到了我們想要的值,這個(gè)設(shè)計(jì)是不是很牛,然后我們看下 class_rw_t 這個(gè)結(jié)構(gòu)體
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;
從結(jié)構(gòu)體重可以看到,累的 方法,屬性,協(xié)議,都保存在這里,然后還有個(gè) const class_ro_t *ro; 這個(gè)是什么呢?
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;
1. baseMethodList 方法列表
2. baseProtocols 協(xié)議列表
3. ivars 成員變量列表
4. baseProperties 屬性列表
5. weakIvarLayout weak 成員變量?jī)?nèi)存布局
6. ivarLayout 成員變量 ivar 內(nèi)存布局,是放在我們的 io 里面的,并且是 const 不允許修改的,也就是說明,我們的 成員變量布局,在編譯階段就確定了,內(nèi)存布局已經(jīng)確定了,在運(yùn)行時(shí)是不可以修改了,這就說明了,為什么運(yùn)行時(shí)不能往類中動(dòng)態(tài)添加成員變量。
class_ro_t 的意思是 readonly 的,在編譯階段就已經(jīng)確定了,不可以修改。
class_ro_t 是只讀的,是再編譯的時(shí)候,將累的屬性,方法,協(xié)議和成員變量,添加到我們的 class_ro_t 中,然后運(yùn)行的時(shí)候,會(huì)動(dòng)態(tài)的創(chuàng)建 class_rw_t 然后將 class_ro_t 和分類中的屬性,協(xié)議方法存儲(chǔ)到 class_rw_t 中,并進(jìn)行排序,分類中的存儲(chǔ)在數(shù)組的前部,原始類信息,存儲(chǔ)在數(shù)組的后面,class_ro_t 是只能的,在運(yùn)行時(shí)是不可以添加進(jìn)去的
class_rw_t 是運(yùn)行時(shí)可以添加的,比如分類中的方法會(huì)在運(yùn)行時(shí),添加到 class_rw_t 的 method_array_t methods; 中去,可以看到,我們的 class_rw_t 中沒有成員變量的信息,成員變量的信息是以編譯就確定添加到 class_ro_t 中去,并且只讀
接著跟著源碼分析下
static Class realizeClassWithoutSwift(Class cls)
{
runtimeLock.assertLocked();
const class_ro_t *ro;
class_rw_t *rw;
Class supercls;
Class metacls;
bool isMeta;
if (!cls) return nil;
if (cls->isRealized()) return cls;
assert(cls == remapClass(cls));
// fixme verify class is not in an un-dlopened part of the shared cache?
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();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
// 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.
// This assumes that none of those classes have Swift contents,
// or that Swift's initializers have already been called.
// fixme that assumption will be wrong if we add support
// for ObjC subclasses of Swift classes.
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa for some classes and/or platforms.
// Set instancesRequireRawIsa.
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
// Non-pointer isa disabled by environment or app SDK version
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && !(ro->flags & RO_META) &&
0 == strcmp(ro->name, "OS_object"))
{
// hack for libdispatch et al - isa also acts as vtable pointer
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
// This is also propagated by addSubclass()
// but nonpointer isa setup needs it earlier.
// Special case: instancesRequireRawIsa does not propagate
// from root class to root metaclass
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsa(rawIsaIsInherited);
}
// SUPPORT_NONPOINTER_ISA
#endif
// 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();
}
}
// Propagate the associated objects forbidden flag from ro or from
// the superclass.
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
// Connect this class to its superclass's subclass lists
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
// Attach categories
methodizeClass(cls);
return cls;
}
我們?cè)?alloc 的時(shí)候,會(huì)調(diào)用上面的方法,然后緊接著有個(gè)判斷
// 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);
}
判斷我們的rw是否創(chuàng)建過了,如果沒有,我們會(huì)創(chuàng)建一個(gè)rw的結(jié)構(gòu)體,然后將ro賦值給rw中的ro,然后將rw賦值給class,
supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));
然后遞歸遍歷他的父類和元類,同樣的方式分配。最后
// Attach categories
methodizeClass(cls);
static void methodizeClass(Class cls)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro;
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
// Install methods and properties that the class implements itself.
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
// Root classes get bonus method implementations if they don't have
// them already. These apply before category replacements.
if (cls->isRootMetaclass()) {
// root metaclass
addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
if (PrintConnecting) {
if (cats) {
for (uint32_t i = 0; i < cats->count; i++) {
_objc_inform("CLASS: attached category %c%s(%s)",
isMeta ? '+' : '-',
cls->nameForLogging(), cats->list[i].cat->name);
}
}
}
if (cats) free(cats);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name));
}
assert(sel_registerName(sel_getName(meth.name)) == meth.name);
}
#endif
}
可以看到
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
rw->methods.attachLists(&list, 1);
}
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rw->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rw->protocols.attachLists(&protolist, 1);
}
是將ro里面改的方法列表啊,屬性列表還有協(xié)議列表,添加到rw里面,然后緊接著
// Attach categories.
category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
attachCategories(cls, cats, false /*don't flush caches*/);
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);
}
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);
將分類中的發(fā)昂發(fā),屬性,協(xié)議,添加到rw中
總結(jié)
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;
最后看一眼我們的 class_ro_t ,有三個(gè)屬性是不允許我們修改的
const uint8_t * ivarLayout;
const char * name;
const ivar_list_t * ivars;
當(dāng)我們初始化一個(gè)類的時(shí)候
- 在編譯的時(shí)候已經(jīng)確定我們的類的原始信息,并將它存儲(chǔ)在 class_ro_t 結(jié)構(gòu)體中,并且運(yùn)行時(shí)不能改變
- 遞歸初始化他的父類和元類
- 將ro中的方法協(xié)議屬性等,添加到rw對(duì)應(yīng)的數(shù)組中
- 將分類中的屬性方法協(xié)議添加到rw中
- 在運(yùn)行時(shí),不能動(dòng)態(tài)的想類中添加成員變量,還有弱引用成員變量,和修改類名
- 因?yàn)檫\(yùn)行時(shí),我們的rw對(duì)ro進(jìn)行了引用,ro的方法列表協(xié)議列表添加到了我們的rw對(duì)用的數(shù)組中,所以就給我們?cè)谶\(yùn)行時(shí)對(duì)方法等做動(dòng)態(tài)修改提供了可能。
很多人可能會(huì)有疑問,runtime 不是提供了 動(dòng)態(tài)添加成員變量的方法 class_addIvar()
,但是蘋果的官方文檔已經(jīng)有明確的說明
This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.
必須在alloc 和 register 之間調(diào)用,之前說過,程序編譯的時(shí)候就生成了成員變量布局,程序啟動(dòng)后就沒有機(jī)會(huì)再添加成員變量了,
因?yàn)槲覀兊念悓?shí)例是需要一塊內(nèi)存空間的,他有isa指針指向,如果我們?cè)谶\(yùn)行時(shí)允許動(dòng)態(tài)修改成員變量的布局,那么創(chuàng)建出來的類實(shí)例就屬于無效的了,能夠被所以修改,但是屬性和方法是我們 objc_class 可以管理的,增刪改都不影響我們實(shí)例內(nèi)存布局。