Runtime做爲Objective-C的運行時的核心一直是iOS開發者必需要學習瞭解的,從事iOS開發也有一段時間,以前都是斷斷續續看Runtime的源碼或者是經過別人的博客思路來學習瞭解Runtime。從今年三月份開始系統的研讀了Runtime的源碼、發現大體和以前瞭解的相同(網上各路大神太多了,攤手臉!),恰好最近項目不忙,因此決定寫幾篇博客記錄下這段經歷,LET'S GO。git
經過翻碼發現蘋果在庫初始化以前由dyld程序註冊了三個通知程序,來實現整個RunTime系統的組建、+load的調用、以及資源釋放。github
_objc_init源碼實現以下:swift
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
//Alex註釋: 讀取Runtime相關的環境變量
environ_init();
tls_init();
static_init();
lock_init();
//Alex註釋: 初始化libobjc異常處理系統
exception_init();
/* Alex註釋: 重要的來了,註冊通知程序:這裏經過名字咱們能夠猜想出蘋果經過dyld註冊了
* 三個程序分別是map_images、load_images、unmap_image經過名字能夠大概猜想出他們
* 的做用、接下來讓咱們來揭開這層面紗。
* ***************這裏比較遺憾的是沒有找到_dyld_objc_notify_register實現的源碼
* ***************若是有大神知道歡迎評論指出
*/
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
複製代碼
dyld源碼 感興趣的能夠去了解。數組
蘋果對於map_images的註釋以下: Process the given images which are being mapped in by dyld. Calls ABI-agnostic code after taking ABI-specific locks. Google翻譯以後大體意思是處理由dyld映射的給定images,獲取ABI鎖並調用與ABI無關的代碼。 images:網上不少翻譯爲鏡像,可是我看源碼則更像是資源文件
解析。緩存
map_images的源碼實現以下:安全
void map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
//Alex註釋: 開啓Runtime鎖
mutex_locker_t lock(runtimeLock);
//Alex註釋: 將具體任務交由map_images_nolock執行
return map_images_nolock(count, paths, mhdrs);
}
複製代碼
map_images將具體的處理交由map_images_nolock方法執行,map_images_nolock實現大概150行代碼,其主要作了下面這些事情:bash
map_images_nolock源碼(有精簡)實現以下:app
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
//Alex註釋: 初始化header_info結構體數組
header_info *hList[mhCount];
//Alex註釋: 用於記錄hList的大小
uint32_t hCount;
//Alex註釋: 用於記錄方法和消息的個數
size_t selrefCount = 0;
if (firstTime) {
//Alex註釋: 獲取共享緩存的內存區域
preopt_init();
}
hCount = 0;
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
/*Alex註釋:
* 將mhdr 轉化爲 header_info 添加到 FirstHeader 鏈表中;若是添加成功,addHeader 返回 header_info 結構體 else NULL。
*/
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
/*Alex註釋:
* 獲取 header_info中的sel數量,這裏區分了__OBJC2__和其餘版本、是由於OBJC
* 2以後提出了消息概念、將以前的SEL變爲了message_ref_t { IMP imp; SEL
* sel;};的結構體封裝,而這正是後來黑魔法實現的基本原理。
*/
#if __OBJC2__
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
}
hList[hCount++] = hi;
}
}
if (firstTime) {
/*Alex註釋:
* 初始化全局的namedSelectors(NXMapTable類型)變量,並註冊必要的的方法如 load、initialize、retain、release、autorelease、retainCount等...
*/
sel_init(selrefCount);
/*Alex註釋:
* 初始化自動釋放池AutoreleasePool(AutoreleasePoolPage)、全局SideTableBuf變量(StripedMap<SideTable>)
* SideTableBuf是Runtime的核心、
關於SideTableBuf本人的其餘博客有介紹歡迎翻閱。
*/
arr_init();
}
if (hCount > 0) {
/*Alex註釋:
* 通過以上處理將mach_header結構體數據轉化爲了header_info結構體數據,接着蘋果將header_info的處理轉交給了_read_images方法去實現。
*/
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
}
複製代碼
蘋果對於_read_images的註釋以下: Perform initial processing of the headers in the linked list beginning with headerList. Called by: map_images_nolock Google翻譯以後大意爲: 對連接中的header_info列表執行初始化處理,由map_images_nolock調用ide
_read_images主要作了以下這些事情:佈局
接下來經過源碼逐行來了解蘋果是如何處理header_info列表的,
_read_images(有精簡)源碼實現以下:
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil;
size_t resolvedFutureClassCount = 0;
static bool doneOnce;
runtimeLock.assertLocked();
//Alex註釋:首次加載初始化
if (!doneOnce) {
doneOnce = YES;
//Alex註釋:ISA適配
#if SUPPORT_NONPOINTER_ISA
# if SUPPORT_INDEXED_ISA
//Alex註釋: SwiftVersion3以前的版本不支持NonpointerIsa
for (EACH_HEADER) {
if (hi->info()->containsSwift() &&
hi->info()->swiftVersion() < objc_image_info::SwiftVersion3)
{
DisableNonpointerIsa = true;
break;
}
}
# endif
# if TARGET_OS_OSX
//Alex註釋: OS X 10.11以前的版本不支持NonpointerIsa
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
DisableNonpointerIsa = true;
}
//Alex註釋: 有__DATA,__objc_rawisa段的不支持NonpointerIsa
for (EACH_HEADER) {
if (hi->mhdr()->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
DisableNonpointerIsa = true;
}
break;
}
# endif
#endif
//Alex註釋: 是否禁用TaggedPointers
if (DisableTaggedPointers) {
disableTaggedPointers();
}
//Alex註釋: 初始化混淆器(隨機產生),保護代碼安全。
initializeTaggedPointerObfuscator();
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
/*Alex註釋:
* gdb_objc_realized_classes 保存不在動態共享緩存(dyld shared cache)中的 命名類 -> hash表
* allocatedClasses 緩存已經被objc_allocateClassPair方法初始化的類和元類 -> hash表
*/
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
allocatedClasses = NXCreateHashTable(NXPtrPrototype, 0, nil);
}
for (EACH_HEADER) {
//Alex註釋: 加載每一個header_info結構體的Classref_t結構體列表
classref_t *classlist = _getObjc2ClassList(hi, &count);
if (! mustReadClasses(hi)) {
continue;
}
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->isPreoptimized();
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
/*Alex註釋:
* readClass主要工做以下:
* 一、從future_named_class_map(NXMapTable)
* 全局對象中查找未實現的newCls,取出該Class的(class_rw_t)data數據
* (rwNew),將cls數據拷貝到newCls,將拷貝後的newCls的
* (class_rw_t)data強轉爲class_ro_t並賦值到rwNew的ro成員變量,
* 最後將rwNew賦值給newCls的(class_rw_t)data
* 二、將newCls添加到全局變量(NXMapTable)gdb_objc_realized_classes的
* MapTable中,gdb_objc_realized_classes保存不在動態共享緩存中的類
* 不管是否實現
* 三、將newCls添加到全局變量(NXMapTable)allocatedClasses的
* MapTable中,allocatedClasses表保存已經Allocated的類
*/
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
//Alex註釋: newCls添加進數組
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
*******************************須要仔細閱讀源碼
//Alex註釋: noClassesRemapped建立remapped_class_map靜態變量(NXMapTable),
if (!noClassesRemapped()) {
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
//Alex註釋: 返回實時的類指針,該指針可能指向已經從新分配內存的類結構
remapClassRef(&classrefs[i]);
}
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
//Alex註釋: 將SEL保存到全局的namedSelectors(NXMapTable表)中
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->isPreoptimized()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
sels[i] = sel_registerNameNoLock(name, isBundle);
}
}
}
//Alex註釋: 加載protocols,並保存到靜態變量(NXMapTable)protocol_map中
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
assert(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->isPreoptimized();
bool isBundle = hi->isBundle();
protocol_t **protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
//Alex註釋: 加載ProtocolRefs,並保存到靜態變量(NXMapTable)protocol_map中
for (EACH_HEADER) {
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
//Alex註釋: 加載非惰性類,用於+load和靜態的instance
for (EACH_HEADER) {
classref_t *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
#if TARGET_OS_SIMULATOR
if (cls->cache._buckets == (void*)&_objc_empty_cache &&
(cls->cache._mask || cls->cache._occupied))
{
cls->cache._mask = 0;
cls->cache._occupied = 0;
}
if (cls->ISA()->cache._buckets == (void*)&_objc_empty_cache &&
(cls->ISA()->cache._mask || cls->ISA()->cache._occupied))
{
cls->ISA()->cache._mask = 0;
cls->ISA()->cache._occupied = 0;
}
#endif
*******************************敲重點:::須要仔細閱讀源碼
//Alex註釋:將cls添加到allocatedClasses全局map中
addClassTableEntry(cls);
/* Alex註釋: 加載非惰性類、執行初始化
* 遞歸佈局class、superClass、metaClass的樹形結構
* reconcileInstanceVariables方法協調實例變量的偏移量,內存佈局
*/
realizeClass(cls);
}
}
//Alex註釋:實現新的未解決的將來類?????????
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
realizeClass(resolvedFutureClasses[i]);
resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
}
free(resolvedFutureClasses);
}
//Alex註釋:加載類別(categories)列表
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
if (!cls) {
catlist[i] = nil;
continue;
}
bool classExists = NO;
//Alex註釋:判斷是否有類的實例方法、協議、實例屬性
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
//Alex註釋:category存放在靜態的MapTable(靜態變量category_map)中,
* 蘋果是將category和header_info封裝成locstamped_category_t結構體,
* 以後將(locstamped_category_t){category, header_info} 添加到全局
* 變量category_map中category->cls映射的locstamped_category_t結構
* 體數組中
*/
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
//Alex註釋: 若是類已經初始化將category的方法、屬性、協議追加到類的
* class_rw_t結構體對應的methods、properties、protocols成員
* 變量中
* 添加到class_rw_t結構體的操做是在attachCategories方法中實現的。
*/
remethodizeClass(cls);
classExists = YES;
}
}
//Alex註釋:這裏處理的是元類的方法、協議、屬性
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
//Alex註釋:調試不易碎的Ivars
if (DebugNonFragileIvars) {
//Alex註釋:非惰性的實現全部已知的未實現的類
realizeAllClasses();
}
//Alex註釋:打印預選的方法列表、優化方法列表、所有的Classes、優化的Classes, DEBUG
if (PrintPreopt) {
static unsigned int PreoptTotalMethodLists;
static unsigned int PreoptOptimizedMethodLists;
static unsigned int PreoptTotalClasses;
static unsigned int PreoptOptimizedClasses;
for (EACH_HEADER) {
classref_t *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
PreoptTotalClasses++;
if (hi->isPreoptimized()) {
PreoptOptimizedClasses++;
}
const method_list_t *mlist;
if ((mlist = ((class_ro_t *)cls->data())->baseMethods())) {
PreoptTotalMethodLists++;
if (mlist->isFixedUp()) {
PreoptOptimizedMethodLists++;
}
}
if ((mlist=((class_ro_t *)cls->ISA()->data())->baseMethods())) {
PreoptTotalMethodLists++;
if (mlist->isFixedUp()) {
PreoptOptimizedMethodLists++;
}
}
}
}
}
#undef EACH_HEADER
}
複製代碼
通過以上處理類的裝載已經完成、而後執行load_images方法、處理已經被加載的類的+load方法。
蘋果對於load_images的註釋以下: Process +load in the given images which are being mapped in by dyld. Google翻譯後大意是:處理在dyld被mapped的images的+load方法。
load_images的代碼並很少,主要是對load_images執行遞歸鎖操做,以後調用prepare_load_methods裝載+load方法,最後經過call_load_methods調用Class和Category的+load方法。
void load_images(const char *path __unused, const struct mach_header *mh)
{
if (!hasLoadMethods((const headerType *)mh)) return;
//Alex註釋: loadMethod加鎖
recursive_mutex_locker_t lock(loadMethodLock);
{
mutex_locker_t lock2(runtimeLock);
//Alex註釋: 裝載+load方法
prepare_load_methods((const headerType *)mh);
}
//Alex註釋: 調用+load方法
call_load_methods();
}
複製代碼
咱們知道category和class均可以實現+load方法,下面經過源碼來細究Category和Class的+load方法是如何實現的。
prepare_load_methods方法實現比較簡單、主要是對mhdr中的Class和Category進行遍歷、最後將+load的方法處理分發到了schedule_class_load和add_category_to_loadable_list方法、分別用於裝載Class的+load和Category的+load方法。
prepare_load_methods源碼實現以下:
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
/*Alex註釋:
* 加載類的 +load方法,並將Class的load方法和類保存到全局的
* (struct loadable_class *)loadable_classes 結構體數組中
*/
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue;
realizeClass(cls);
assert(cls->ISA()->isRealized());
/*Alex註釋:
* 加載category的 +load方法, 並將category的load方法和category存儲到
* (struct loadable_category *)loadable_categories 結構體數組中
*/
add_category_to_loadable_list(cat);
}
}
複製代碼
關於類的+load方法的裝載、經過源碼能夠看出蘋果是採用遞歸的方式從父類到子類逐個遍歷,最後將具體的裝載處理交由add_class_to_loadable_list方法執行。
add_class_to_loadable_list實現就更加簡明瞭,大體分爲下面幾步:
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized());
if (cls->data()->flags & RW_LOADED) return;
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
<!-- add_class_to_loadable_list 實現 {
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return;
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
} -->
cls->setInfo(RW_LOADED);
}
複製代碼
關於Category的+load方法的裝載,其實和類的裝載大體相同。只是這裏在調用add_category_to_loadable_list方法裝載時、若是Class沒有realize的話會對Class進行realize,還要注意的是Category的+load方法是被單獨保存在一個全局的struct loadable_category *loadable_categories 靜態變量中的,他和類的+load方法是分開存放的。
+load方法裝載以後蘋果經過call_load_methods方法來調用Class和Category的+load方法。Class和Category的+load調用順序在這裏也能夠獲得想要的答案。
call_load_methods的實現源碼以下:
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
//Alex註釋: 循環調用Class的+load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
//Alex註釋:調用category的+load方法,僅調用一次
more_categories = call_category_loads();
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
複製代碼
類的+load的調用源碼:
static void call_class_loads(void)
{
int i;
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
//Alex註釋:遍歷調用Class的+load方法
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
//Alex註釋:調用+load方法
(*load_method)(cls, SEL_load);
}
if (classes) free(classes);
}
複製代碼
Category的+load方法的調用源碼:
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
//Alex註釋: 循環調用Category的+load方法
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
//Alex註釋:調用+load方法
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
shift = 0;
for (i = 0; i < used; i++) {
if (cats[i].cat) {
cats[i-shift] = cats[i];
} else {
shift++;
}
}
used -= shift;
new_categories_added = (loadable_categories_used > 0);
for (i = 0; i < loadable_categories_used; i++) {
if (used == allocated) {
allocated = allocated*2 + 16;
cats = (struct loadable_category *)
realloc(cats, allocated *
sizeof(struct loadable_category));
}
cats[used++] = loadable_categories[i];
}
if (loadable_categories) free(loadable_categories);
if (used) {
loadable_categories = cats;
loadable_categories_used = used;
loadable_categories_allocated = allocated;
} else {
if (cats) free(cats);
loadable_categories = nil;
loadable_categories_used = 0;
loadable_categories_allocated = 0;
}
return new_categories_added;
}
複製代碼
unmap_image對應map_images有map操做就有對應的unmap。 蘋果對於unmap的註釋以下: Process the given image which is about to be unmapped by dyld. Google翻譯大意爲:處理由dyld取消映射的image。
unmap_image的源碼以下:
void unmap_image(const char *path __unused, const struct mach_header *mh)
{
recursive_mutex_locker_t lock(loadMethodLock);
mutex_locker_t lock2(runtimeLock);
//Alex註釋: 加鎖將具體的任務轉交unmap_image_nolock方法
unmap_image_nolock(mh);
}
複製代碼
從全局的header_info結構體鏈表中遍歷查找Runtime的header_info結構體,調用_unload_image處理header_info結構體、最後將其從鏈表中移除。
void unmap_image_nolock(const struct mach_header *mh)
{
header_info *hi;
for (hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
if (hi->mhdr() == (const headerType *)mh) {
break;
}
}
if (!hi) return;
_unload_image(hi);
removeHeader(hi);
free(hi);
}
複製代碼
void _unload_image(header_info *hi)
{
size_t count, i;
loadMethodLock.assertLocked();
runtimeLock.assertLocked();
//Alex註釋: 中止附加Category和調用Category的+load方法
category_t **catlist = _getObjc2CategoryList(hi, &count);
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
if (!cat) continue;
Class cls = remapClass(cat->cls);
assert(cls);
removeUnattachedCategoryForClass(cat, cls);
remove_category_from_loadable_list(cat);
}
NXHashTable *classes = NXCreateHashTable(NXPtrPrototype, 0, nil);
classref_t *classlist;
//Alex註釋: 從header_info結構體中加載已經remap的Classs
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);
}
NXHashState hs;
Class cls;
hs = NXInitHashState(classes);
while (NXNextHashState(classes, &hs, (void**)&cls)) {
//Alex註釋: 將loadable表中的類移除
remove_class_from_loadable_list(cls);
//Alex註釋: 將類、元類從已經初始化的全局靜態表中移除
detach_class(cls->ISA(), YES);
detach_class(cls, NO);
}
hs = NXInitHashState(classes);
while (NXNextHashState(classes, &hs, (void**)&cls)) {
//Alex註釋: 釋放類和元類指針
free_class(cls->ISA());
free_class(cls);
}
//Alex註釋: 釋放hashTable
NXFreeHashTable(classes);
}
複製代碼
此次源碼閱讀學習到了類是如何從共享緩存、讀取、轉化、以及初始化這樣一個裝載過程,學到了類、方法、協議等在內存中存儲的形式以及類、元類、超類之間的關係。在load_images模塊也證明了以前關於category和class中+load方法的認知。 將來可期,加油少年!!