iOS開(kāi)發(fā)-class_ro_t和class_rw_t的區(qū)別
文章目錄
class_ro_t
class_ro_t存儲(chǔ)了當(dāng)前類(lèi)在編譯期就已經(jīng)確定的屬性、方法以及遵循的協(xié)議,里面是沒(méi)有分類(lèi)的方法的。那些運(yùn)行時(shí)添加的方法將會(huì)存儲(chǔ)在運(yùn)行時(shí)生成的class_rw_t中。
ro即表示read only,是無(wú)法進(jìn)行修改的。
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;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
class_rw_t
ObjC 類(lèi)中的屬性、方法還有遵循的協(xié)議等信息都保存在 class_rw_t中:
// 可讀可寫(xiě)
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; // 指向只讀的結(jié)構(gòu)體,存放類(lèi)初始信息
/*
這三個(gè)都是二位數(shù)組,是可讀可寫(xiě)的,包含了類(lèi)的初始內(nèi)容、分類(lèi)的內(nèi)容。
methods中,存儲(chǔ) method_list_t ----> method_t
二維數(shù)組,method_list_t --> method_t
這三個(gè)二位數(shù)組中的數(shù)據(jù)有一部分是從class_ro_t中合并過(guò)來(lái)的。
*/
method_array_t methods; // 方法列表(類(lèi)對(duì)象存放對(duì)象方法,元類(lèi)對(duì)象存放類(lèi)方法)
property_array_t properties; // 屬性列表
protocol_array_t protocols; //協(xié)議列表
Class firstSubclass;
Class nextSiblingClass;
//...
}
class_rw_t生成時(shí)機(jī)
class_rw_t生成在運(yùn)行時(shí),在編譯期間,class_ro_t結(jié)構(gòu)體就已經(jīng)確定,objc_class中的bits的data部分存放著該結(jié)構(gòu)體的地址。在runtime運(yùn)行之后,具體說(shuō)來(lái)是在運(yùn)行runtime的realizeClass 方法時(shí),會(huì)生成class_rw_t結(jié)構(gòu)體,該結(jié)構(gòu)體包含了class_ro_t,并且更新data部分,換成class_rw_t結(jié)構(gòu)體的地址。
類(lèi)的realizeClass運(yùn)行之前:
類(lèi)的realizeClass運(yùn)行之后:
細(xì)看兩個(gè)結(jié)構(gòu)體的成員變量會(huì)發(fā)現(xiàn)很多相同的地方,他們都存放著當(dāng)前類(lèi)的屬性、實(shí)例變量、方法、協(xié)議等等。區(qū)別在于:class_ro_t存放的是編譯期間就確定的;而class_rw_t是在runtime時(shí)才確定,它會(huì)先將class_ro_t的內(nèi)容拷貝過(guò)去,然后再將當(dāng)前類(lèi)的分類(lèi)的這些屬性、方法等拷貝到其中。所以可以說(shuō)class_rw_t是class_ro_t的超集,當(dāng)然實(shí)際訪問(wèn)類(lèi)的方法、屬性等也都是訪問(wèn)的class_rw_t中的內(nèi)容
分類(lèi)方法加載到class_rw_t的流程
- 程序啟動(dòng)后,通過(guò)編譯之后,
Runtime會(huì)進(jìn)行初始化,調(diào)用_objc_init。
_objc_init由dyld驅(qū)動(dòng),這個(gè)階段會(huì)注冊(cè)3個(gè)回調(diào),分別是mapped,init,unmapped
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init(); //環(huán)境調(diào)試 例如僵尸模式設(shè)置后,就是在這里起作用的
tls_init(); //tls指的是局部線程存儲(chǔ),可以將數(shù)據(jù)存儲(chǔ)在線程一個(gè)公共區(qū)域,例如pthread_setspecific(),在autoreleasepool和堆棧信息獲取時(shí)都有涉及
static_init(); //執(zhí)行c++靜態(tài)構(gòu)造函數(shù)
lock_init(); //這里獲取兩個(gè)的線程優(yōu)先級(jí) 后臺(tái)優(yōu)先級(jí)線程以及主線程
exception_init(); //這里初始化libobjc的exception處理系統(tǒng)
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
比較核心的是_dyld_objc_notify_register方法
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
map_images
- 然后會(huì)
map_images。
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
rwlock_writer_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
- 接下來(lái)調(diào)用
map_images_nolock。
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
//.... 略去一大塊
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
//...
}
- 再然后就是
_read_images,這個(gè)方法會(huì)讀取所有的類(lèi)的相關(guān)信息。根據(jù)注釋?zhuān)?code>_read_images 方法主要做了下面這些事情:
_read_images 方法寫(xiě)了很長(zhǎng),其實(shí)就是做了一件事,將Mach-O文件的section依次讀取,并根據(jù)內(nèi)容初始化runtime的內(nèi)存結(jié)構(gòu)。
- 是否需要禁用
isa優(yōu)化。這里有三種情況:使用了swift 3.0前的swift代碼。OSX版本早于10.11。在OSX系統(tǒng)下,Mach-O的DATA段明確指明了__objc_rawisa(不使用優(yōu)化的isa).
蘋(píng)果從
ARM64位架構(gòu)開(kāi)始,對(duì)isa進(jìn)行了優(yōu)化,將其定義成一個(gè)共用體(union)結(jié)構(gòu),結(jié)合位域的概念以及位運(yùn)算的方式來(lái)存儲(chǔ)更多類(lèi)相關(guān)信息。isa指針需要通過(guò)與一個(gè)叫ISA_MASK的值(掩碼)進(jìn)行二進(jìn)制&運(yùn)算,才能得到真實(shí)的class/meta-class對(duì)象的地址。
參考文章 http://m.itdecent.cn/p/30de582dbeb7
- 判斷是否禁用了
tagged pointer - 在
__objc_classlist section中讀取class list - 在
__objc_classrefs section中讀取class引用的信息,并調(diào)用remapClassRef方法來(lái)處理。 - 在
__objc_selrefs section中讀取selector的引用信息,并調(diào)用sel_registerNameNoLock方法處理。 - 在
__objc_protolist section中讀取cls的Protocol信息,并調(diào)用readProtocol方法來(lái)讀取Protocol信息。 - 在
__objc_protorefs section中讀取protocol的ref信息,并調(diào)用remapProtocolRef方法來(lái)處理。 - 在
__objc_nlclslist section中讀取non-lazy class信息,并調(diào)用static Class realizeClass(Class cls)方法來(lái)實(shí)現(xiàn)這些class。realizeClass方法核心是初始化objc_class數(shù)據(jù)結(jié)構(gòu),賦予初始值。 - 在
__objc_catlist section中讀取category信息,并調(diào)用addUnattachedCategoryForClass方法來(lái)為類(lèi)或元類(lèi)添加對(duì)應(yīng)的方法,屬性和協(xié)議。
- 調(diào)用
reMethodizeClass:,這個(gè)方法是重新方法化的意思。 - 在
reMethodizeClass:方法內(nèi)部會(huì)調(diào)用attachCategories:,這個(gè)方法會(huì)傳入Class和Category,會(huì)將方法列表,協(xié)議列表等與原有的類(lèi)合并。最后加入到class_rw_t結(jié)構(gòu)體中。
load_images
構(gòu)造好 class_rw_t 之后,load_images 調(diào)用 call_load_methods 就是開(kāi)始調(diào)用類(lèi)的+load方法和分類(lèi)的+load方法了
/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class's +load.
*
* This method must be RE-ENTRANT, because a +load could trigger
* more image mapping. In addition, the superclass-first ordering
* must be preserved in the face of re-entrant calls. Therefore,
* only the OUTERMOST call of this function will do anything, and
* that call will handle all loadable classes, even those generated
* while it was running.
*
* The sequence below preserves +load ordering in the face of
* image loading during a +load, and make sure that no
* +load method is forgotten because it was added during
* a +load call.
* Sequence:
* 1\. Repeatedly call class +loads until there aren't any more
* 2\. Call category +loads ONCE.
* 3\. Run more +loads if:
* (a) there are more classes to load, OR
* (b) there are some potential category +loads that have
* still never been attempted.
* Category +loads are only run once to ensure "parent class first"
* ordering, even if a category +load triggers a new loadable class
* and a new loadable category attached to that class.
*
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1\. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2\. Call category +loads ONCE
more_categories = call_category_loads();
// 3\. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
unmap_image
unmap_image 調(diào)用 _unload_image
涉及一些資源的釋放,例如 unattached list ,+load queue,將每個(gè)類(lèi)分離后,進(jìn)行釋放
/***********************************************************************
* _unload_image
* Only handles MH_BUNDLE for now.
* Locking: write-lock and loadMethodLock acquired by unmap_image
**********************************************************************/
void _unload_image(header_info *hi)
{
size_t count, I;
loadMethodLock.assertLocked();
runtimeLock.assertWriting();
// Unload unattached categories and categories waiting for +load.
category_t **catlist = _getObjc2CategoryList(hi, &count);
for (i = 0; i < count; i++) {
category_t *cat = catlist[I];
if (!cat) continue; // category for ignored weak-linked class
Class cls = remapClass(cat->cls);
assert(cls); // shouldn't have live category for dead class
// fixme for MH_DYLIB cat's class may have been unloaded already
// unattached list
removeUnattachedCategoryForClass(cat, cls);
// +load queue
remove_category_from_loadable_list(cat);
}
// Unload classes.
// Gather classes from both __DATA,__objc_clslist
// and __DATA,__objc_nlclslist. arclite's hack puts a class in the latter
// only, and we need to unload that class if we unload an arclite image.
NXHashTable *classes = NXCreateHashTable(NXPtrPrototype, 0, nil);
classref_t *classlist;
classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (cls) NXHashInsert(classes, cls);
}
classlist = _getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (cls) NXHashInsert(classes, cls);
}
// First detach classes from each other. Then free each class.
// This avoid bugs where this loop unloads a subclass before its superclass
NXHashState hs;
Class cls;
hs = NXInitHashState(classes);
while (NXNextHashState(classes, &hs, (void**)&cls)) {
remove_class_from_loadable_list(cls);
detach_class(cls->ISA(), YES);
detach_class(cls, NO);
}
hs = NXInitHashState(classes);
while (NXNextHashState(classes, &hs, (void**)&cls)) {
free_class(cls->ISA());
free_class(cls);
}
NXFreeHashTable(classes);
// XXX FIXME -- Clean up protocols:
// <rdar://problem/9033191> Support unloading protocols at dylib/image unload time
// fixme DebugUnload
}