筆記02

Category的用途:

1.類的拆解:根據(jù)不同功能將代碼放入不同分類中(模擬多繼承),減少單個類的體積;
2.公開“私有方法”(私有方法前向引用):類擴展中已聲明私有方法次洼,去分類.h聲明;

  • (a) 類擴展中聲明私有方法
@interface Person ()
// 類擴展聲明的私有方法
- (void)ExtensionPrivateMethod ;
@end

@implementation Person
- (void)ExtensionPrivateMethod {
    NSLog(@"類擴展@interface()有聲明,私有方法");
}
@end
  • (b) 分類.h再次聲明
#import "Person.h"
@interface Person (Category1)
// .h中聲明私有方法遇骑,.m中無需實現(xiàn)
- (void)ExtensionPrivateMethod;
@end

#import "Person+Category1.h"
@implementation Person (Category1)
@end
  • (c) 調(diào)用私有方法卖毁,需要引入類和分類
#import "Person.h"
#import "Person+Category1.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        // 私有方法前向引用
        [person ExtensionPrivateMethod];
    }
    return 0;
}

總結(jié):
1.如果不在分類.h中公開私有方法,可以通過performSelector方式強制調(diào)用私有方法或者拿到它的函數(shù)指針調(diào)用
2.正常的方式調(diào)用就需要在分類.h中公開(聲明)私有方法落萎,不實現(xiàn)

Person *person = [[Person alloc] init];
[person performSelector:@selector(ExtensionPrivateMethod)];

Category結(jié)構(gòu)

分類不能直接添加成員變量亥啦,因為category中沒有存儲成員變量對應(yīng)的指針變量;
可以利用關(guān)聯(lián)對象技術(shù)實現(xiàn)為category添加成員變量的效果练链;
分類能添加屬性翔脱,但是不會生成setter和getter方法;

struct category_t {
    const char *name;                           // 分類的名稱
    classref_t cls;                             // 分類所屬的列
    struct method_list_t *instanceMethods;      // 添加的實例方法列表
    struct method_list_t *classMethods;         // 添加的類方法列表
    struct protocol_list_t *protocols;          // 添加的協(xié)議列表
    struct property_list_t *instanceProperties; // 添加的實例屬性列表
    struct property_list_t *_classProperties;   // 類屬性(class, nonatomic, strong)
}
  • 關(guān)聯(lián)對象技術(shù)實現(xiàn)為分類添加成員變量
#import "Person+Category.h"
#import <objc/runtime.h> // 類相關(guān)函數(shù)
//#import <objc/message.h> // 消息相關(guān)函數(shù)

@implementation Person (Category)

// 關(guān)聯(lián)對象 能夠為分類添加屬性
- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN);
}

- (NSString *)name {
    return objc_getAssociatedObject(self, @"name");
}

@end

關(guān)聯(lián)對象API
設(shè)置類關(guān)聯(lián)屬性 void _object_set_associative_reference(object, key, value, policy)
獲取類關(guān)聯(lián)屬性 id _object_get_associative_reference(object, key);
移除類所有關(guān)聯(lián)屬性 void _object_remove_assocations(object);

  • 設(shè)置類關(guān)聯(lián)屬性 _object_set_associative_reference(object, key, value, policy)
/**
@param object 準備被關(guān)聯(lián)的對象
@param key 要關(guān)聯(lián)的值對應(yīng)的key 標識
@param value 關(guān)聯(lián)的值
@param policy 策略
 */
void _object_set_associative_reference(id object, void *key, id value,  uintptr_t policy) {
    // 存放 policy 和 關(guān)聯(lián)值value 默認為空
    ObjcAssociation old_association(0, nil);
                                           
    // 根據(jù) 策略policy 對 value 進行加工媒鼓,按照策略對value進行 copy或者retain
    // newValue ---> 準備關(guān)聯(lián)的值
    id new_value = value ? acquireValue(value, policy) : nil;
  
    {
        // 關(guān)聯(lián)對象管理類届吁,C++實現(xiàn)一個類  
        // 維護了一個單例Hash表 AssociationsHashMap對象
        AssociationsManager manager;
        
        /**
         初始化一個 AssociationsHashMap 對象 associations
         用來維護對象 和 ObjectAssociationMap 之間的關(guān)系
         單例對象 AssociationsHashMap
         */
        AssociationsHashMap &associations(manager.associations());
        
        // 獲取關(guān)聯(lián)對象的索引 ---> DISGUISE 對這個指針地址 按位取反
        disguised_ptr_t disguised_object = DISGUISE(object);

        if (new_value) {
            // 根據(jù)對象指針查找對應(yīng)的一個ObjectAssociateMap結(jié)構(gòu)的map
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            
            // 從全局容器中找到了ObjectAssociationMap
            if (i != associations.end()) {
                // secondary table exists  -->  i->second
                // i->first 表示對象指針
                // i->second 表示獲取 ObjectAssociationMap
                ObjectAssociationMap *refs = i->second;
                // 根據(jù)傳遞進來的key進行查找
                ObjectAssociationMap::iterator j = refs->find(key);

                if (j != refs->end()) { // ObjcAssociation 找到了
                    old_association = j->second;
                    // 如果關(guān)聯(lián)對象已存在,則通過ObjcAssociation賦新值(替換最新的value)
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    // 如果關(guān)聯(lián)對象不存在隶糕,則創(chuàng)建新的關(guān)聯(lián)對象
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                /**
                 create the new association (first time).
                 如果沒有 ObjectAssociationMap 表
                 則第一次創(chuàng)建一個 ObjectAssociationMap 表
                 */
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                // 全局容器中的key = disguised_object
                // 全局容器中的value = 這個新創(chuàng)建的map
                associations[disguised_object] = refs;
                /**
                新關(guān)聯(lián)的值 new_value 通過策略 policy 組裝成 ObjcAssociation 
                作為新創(chuàng)建好的map, ObjectAssociationMap[key]
                key 傳進來的key
                */
                (*refs)[key] = ObjcAssociation(policy, new_value);
                // 最后設(shè)置這個對象是有關(guān)聯(lián)對象
                object->setHasAssociatedObjects();
            }
        } else {
            /**
             如果new_value為 空瓷产,那么刪除該關(guān)聯(lián)對象
             */
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            // 查找到了這個ObjectAssociationMap
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                // 通過key 到 ObjectAssociationMap中查找
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    // 找到了ObjcAssociation
                    old_association = j->second;
                    // 擦除操作站玄,從ObjectAssociationMap中移除
                    // 所以我們想移除關(guān)聯(lián)對象枚驻,可以將關(guān)聯(lián)對象的值設(shè)置為nil進行移除
                    refs->erase(j);
                }
            }
        }
    }
    // 釋放舊值
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
  • 獲取類關(guān)聯(lián)屬性 _object_get_associative_reference(object, key)
// 用來得到已有的關(guān)聯(lián)對象
id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
        // 初始化AssociationsManager
        AssociationsManager manager;
        /**
         初始化一個AssociationsHashMap 對象 associations
         用來維護對象和ObjectAssociationMap之間的關(guān)系
         */
        AssociationsHashMap &associations(manager.associations());
        // 獲取關(guān)聯(lián)對象的索引
        disguised_ptr_t disguised_object = DISGUISE(object);
        // 根據(jù)對象指針查找對應(yīng)的一個ObjectAssociateMap結(jié)構(gòu)的map
        // 通過迭代器 找到對應(yīng)的 ObjectAssociationMap
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        
        if (i != associations.end()) {
            // i->first 表示對象地址
            // i->second表示獲取ObjectAssociationMap
            ObjectAssociationMap *refs = i->second;
            // 通過find(key)找到ObjectAssociationMap
            ObjectAssociationMap::iterator j = refs->find(key);
            if (j != refs->end()) {
                // 獲取到ObjcAssociation
                ObjcAssociation &entry = j->second;
                // 取值 取出value和policy
                value = entry.value();
                policy = entry.policy();
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                    objc_retain(value);
                }
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        objc_autorelease(value);
    }
    return value;
}
  • 移除類所有關(guān)聯(lián)對象 _object_remove_assocations(object)
void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // 找到ObjectAssociationMap 開始遍歷
            // 拷貝所有需要刪除的關(guān)聯(lián)對象
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // 刪除 ObjectAssociationMap
            delete refs;
            associations.erase(i);
        }
    }
    // 將拷貝的值再次遍歷release.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

Category的加載過程

  • _objc_init() runtime入口函數(shù),進行一些初始化操作
void _objc_init(void) {
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    // 注冊了三個回調(diào)函數(shù)
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
  • map_images() 加鎖
void map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[]) {
    // 加鎖
    rwlock_writer_t lock(runtimeLock);
    // 完成所有類的注冊 和 fixup 等操作,還包括一些初始化工作以及 調(diào)用load類方法
    return map_images_nolock(count, paths, mhdrs);
}
  • map_images_nolock() 完成所有類的注冊和fixup等工作,還包括一些初始化工作以及調(diào)用load類方法
void  map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[]) {
    ......
    ......

    /**
    _read_images 完成兩件事
    1掐场、將category和類綁定在一起
    2晰筛、重建類的方法列表
     */
    if (hCount > 0) {
        // 完成類的加載,協(xié)議的加載哟沫,類別的加載等工作
        _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
    }
    firstTime = NO;
}
  • _read_images() 完成類、協(xié)議、分類的加載等工作

_read_images()中主要完成了兩件事情
1沽损、addUnattachedCategoryForClass(...) 將category和類綁定在一起
2、remethodizeClass(...)重建類的方法列表

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) {
    ......
   // 發(fā)現(xiàn)分類
   for (EACH_HEADER) {
        // 從編譯好的文件獲取所有的分類
        category_t **catlist = _getObjc2CategoryList(hi, &count);
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();
        
        // 遍歷category列表
        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;
            if (cat->instanceMethods ||  
                cat->protocols  ||  
                cat->instanceProperties)  {
                // 綁定分類和目標類 or 將category添加到分類表中
                addUnattachedCategoryForClass(cat, cls, hi);
                // 判斷類是否初始化
                if (cls->isRealized()) {
                    // 重新構(gòu)建方法列表
                    remethodizeClass(cls);
                    classExists = YES;
                }
            }

            // 把category的類方法和協(xié)議添加到類的metaclass上(元類)
            if (cat->classMethods  ||
                cat->protocols  ||  
                (hasClassProperties && cat->_classProperties))  {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
            }
        }
    }
    if (DebugNonFragileIvars) {
        realizeAllClasses();
    }
}

// 將category添加到分類表中
static void addUnattachedCategoryForClass(category_t *cat, Class cls, 
                                          header_info *catHeader) {
    runtimeLock.assertWriting();
    // 獲取未處理的分類的表(沒有把分類中的信息添加到類中去)
    // NXMapTable 是哈希表
    NXMapTable *cats = unattachedCategories();
    category_list *list;

    // 根據(jù)類獲取對應(yīng)的分類數(shù)組
    list = (category_list *)NXMapGet(cats, cls);
    if (!list) {
        list = (category_list *)
            calloc(sizeof(*list) + sizeof(list->list[0]), 1);
    } else {
        // 擴容
        list = (category_list *)
            realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
    }
    // 把當前分類添加到分類數(shù)組中 list中是一系列l(wèi)ocstamp_category_t
    list->list[list->count++] = (locstamped_category_t){cat, catHeader};
    // 把新的list插入到分類表中去 key=cls, value=list
    NXMapInsert(cats, cls, list);
}

  • remethodizeClass() 重建類的方法列表
// 重建類的方法列表
static void remethodizeClass(Class cls) {
    category_list *cats;
    bool isMeta;
    runtimeLock.assertWriting();
    // 是否是元類
    isMeta = cls->isMetaClass();
    // 獲取類對應(yīng)的分類數(shù)組循头,并且從分類的哈希表中刪除掉分類數(shù)組
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
        // 將分類的method绵估、protocol炎疆、property添加到class中
        attachCategories(cls, cats, true /*flush caches*/);        
        // 釋放分類
        free(cats);
    }
}

// 獲取cls中未完成整合的所有分類
static category_list *unattachedCategoriesForClass(Class cls, bool realizing) {
    runtimeLock.assertWriting();
    // 返回對應(yīng)的分類
    return (category_list *)NXMapRemove(unattachedCategories(), cls);
}
  • attachCategories() 將分類中的方法和屬性列表綁定到目標類上

attachCategories 主要是創(chuàng)建一個新的方法列表空間,存放category中的實例方法国裳、類方法形入、協(xié)議方法,然后將這個方法列表交給attachLists處理

static void attachCategories(Class cls, category_list *cats, bool flush_caches) {
    // 對分類進行空判斷缝左,如果沒有直接return
    if (!cats) return;
    // 判斷是否是元類
    bool isMeta = cls->isMetaClass();
    /**
     方法列表 二維數(shù)組
     [
        [method_t, method_t],
        [method_t],
        [method_t, method_t,method_t],
        ...
     ]
     */
    // 方法列表
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    // 屬性列表
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    // 協(xié)議列表
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    
    // 原有類的分類的總數(shù)
    int i = cats->count;
    bool fromBundle = NO;
    
    // 遍歷所有分類亿遂,依次將每個分類里面的方法添加到臨時的數(shù)組中
    // 后編譯的分類會先調(diào)用  
    while (i--) { // 倒序遍歷,最先訪問最后編譯的分類
       
        // 獲取該分類的方法列表
        auto& entry = cats->list[I];
        // 返回類的方法列表渺杉,并拼接在臨時的方法數(shù)組中
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);        
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        // 屬性列表添加規(guī)則 同方法列表添加規(guī)則
        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        // 協(xié)議列表添加規(guī)則 同方法列表添加規(guī)則
        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    // 獲取宿主類當中的 rw 數(shù)據(jù)蛇数,其中包含宿主類的方法列表信息
    auto rw = cls->data();

    // 主要是針對 分類中關(guān)于內(nèi)存管理相關(guān)方法情況下的 一些特殊處理
    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);

    /**
     rw 代表類
     methods 代表類的方法列表
     attachLists 方法的含義是 將含有mcount個元素的mlists拼接到 rw 的methods上
     */

    // 1、創(chuàng)建新的 方法列表 和 目標類中方法列表融合
    // 1是越、把分類中的方法添加class中的方法列表中去
    rw->methods.attachLists(mlists, mcount);
    free(mlists);

    if (flush_caches  &&  mcount > 0) flushCaches(cls);
    // 2苞慢、創(chuàng)建新的 屬性列表 和 目標類的屬性列表融合
    // 2、把分類中的屬性添加class中的屬性列表中去
    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    // 3英妓、創(chuàng)建新的 協(xié)議列表 和 目標類的協(xié)議列表融合
    // 3挽放、將分類中的協(xié)議列表添加到class中的協(xié)議列表中去
    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}

  • attachLists() 將類中的方法和分類中的方法放到一個列表中

oldCount:目標類中方法列表長度
addedCount:category方法列表的長度
mommove:將類方法后移addedCount個偏移量
momcpy:將category中方法復(fù)制到偏移量從0到addedCount的類方法列表中
所以category中的方法并沒有覆蓋類中的方法,只是將category中的方法放到了類方法的前面蔓纠,調(diào)用方法的時候辑畦,如果category和類中有同名方法,系統(tǒng)會找到前面category中的方法返回并調(diào)用腿倚。category中的方法優(yōu)先級高于類中的方法

void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;
    
    // 有數(shù)組的情況下
    if (hasArray()) {
        // many lists -> many lists
        // 列表中原有元素總數(shù) oldCount
        uint32_t oldCount = array()->count;

        // 拼接之后的元素總數(shù)
        uint32_t newCount = oldCount + addedCount;

        // 根據(jù)新總數(shù)重新分配內(nèi)存
        setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));

        // 重新設(shè)置元素總數(shù)
        array()->count = newCount;
        /**
         內(nèi)存移動
         [[], [], [], [原有的第一個元素], [原有的第二個元素]]
         */
        memmove(array()->lists + addedCount,
                array()->lists,
                oldCount * sizeof(array()->lists[0]));
        
        /**
         內(nèi)存拷貝
         [
            A ---> [addedLists中的第一個元素]
            B ---> [addedLists中第二個元素]
            C ---> [addedLists中第三個元素]
            [原有的第一個元素]
            [原有的第二個元素]
         ]
         */
        memcpy(array()->lists, addedLists,
               addedCount * sizeof(array()->lists[0]));

    } else if (!list  &&  addedCount == 1) {
        // 0 lists -> 1 list
        // 直接將新方法列表首地址賦值給列表
        list = addedLists[0];
    } else {
        // 1 list -> many lists
        // 類方法列表只有一個
        List* oldList = list;
        uint32_t oldCount = oldList ? 1 : 0;
        uint32_t newCount = oldCount + addedCount;
        setArray((array_t *)malloc(array_t::byteSize(newCount)));
        array()->count = newCount;

        // 將類方法直接移動到列表的addedCount位置
        if (oldList) array()->lists[addedCount] = oldList;
        // 將Category中的方法拷貝到array數(shù)組中位置從0~addedCount-1前面
        memcpy(array()->lists, addedLists,
               addedCount * sizeof(array()->lists[0]));
    }
}

總結(jié):通過Runtime加載某個類的所有category數(shù)據(jù)纯出,將所有category的方法、屬性敷燎、協(xié)議信息合并到一個大數(shù)組中暂筝,后參與編譯的category數(shù)據(jù),會在數(shù)組的前面(后編譯先調(diào)用while(--))硬贯,將合并后的category數(shù)據(jù)(方法焕襟、屬性、協(xié)議)插入到類原來數(shù)據(jù)的前面

Category的"覆蓋"問題

后編譯的category中的同名方法會覆蓋前面編譯的category中的同名方法
category覆蓋類中的同名方法
可以通過函數(shù)指針調(diào)用原有類中的方法

#import "Person.h"
#import "Person+Category1.h"
#import "Person+Category2.h"
#import <objc/runtime.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
  
        Person *person = [[Person alloc] init];
        [person test];

        // 調(diào)用person中的test方法
        unsigned int count = 0;
        Method *methodList = class_copyMethodList([Person class], &count);
        
        IMP imp = NULL;
        SEL sel = NULL;
        for (unsigned int i = 0; i < count; i ++) {
            Method method = methodList[i];
            SEL methodSEL = method_getName(method);
            NSString *methodName = [NSString stringWithUTF8String:sel_getName(methodSEL)];
            if ([methodName isEqualToString:@"test"]) {
                imp = method_getImplementation(method);
                sel = method_getName(method);
            }
        }
        ((void(*)(id, SEL))(void *)imp)(person, sel);
        free(methodList);
    }
    return 0;
}

函數(shù)指針調(diào)用

load方法加載順序

load_images中主要做了2件事情:
1饭豹、發(fā)現(xiàn)load方法 prepare_load_methods();
2鸵赖、調(diào)用load方法 call_load_methods();

void load_images(const char *path __unused, const struct mach_header *mh) {
    // 判斷是否有l(wèi)oad方法,沒有l(wèi)oad方法直接返回
    if (!hasLoadMethods((const headerType *)mh)) return;

    // 遞歸鎖
    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods 發(fā)現(xiàn)load方法
    {
        // 讀寫鎖
        rwlock_writer_t lock2(runtimeLock);
        // 發(fā)現(xiàn)load方法
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    // 調(diào)用load方法
    call_load_methods();
}
  • prepare_load_methods()

prepare_load_methods()中schedule_class_load(isa->superclass) 通過遞歸先將父類中的load方法存入loadable_class拄衰,所有得出父類的load優(yōu)先于子類的load方法調(diào)用

void prepare_load_methods(const headerType *mhdr) {
    size_t count, i;
    runtimeLock.assertWriting();

    // 獲取非懶加載的類的列表
    classref_t *classlist =  _getObjc2NonlazyClassList(mhdr, &count);
        for (i = 0; i < count; i++) {
        // 核心1:【獲得子類和父類中所有的load方法】load 方法如何別處理在這里 先加載父類 再加載子類
        schedule_class_load(remapClass(classlist[i]));
    }

    // 獲得非懶加載category的列表 分類沒有父類
    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;  // category for ignored weak-linked class 忽略弱連接的分類
        // 初始化類信息
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        // 核心2:添加到loadable_categories
        add_category_to_loadable_list(cat);
    }
}

static void schedule_class_load(Class cls) {
    // 判斷是否為空
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize
    
    /**
     // load方法已經(jīng)被執(zhí)行過
     #define RW_LOADED             (1<<23)
     和下面的 cls->setInfo(RW_LOADED)  對應(yīng)
     */
    // 判斷是否已經(jīng)添加過load方法到load方法列表中
    if (cls->data()->flags & RW_LOADED) return;

    // 通過遞歸找到父類的load方法并添加它褪,實際上調(diào)用自身的方法
    schedule_class_load(cls->superclass);

    // 將Class和IMP添加到調(diào)用列表中
    add_class_to_loadable_list(cls);
    // 設(shè)置class狀態(tài)為已經(jīng)添加過load方法 意思是設(shè)置了一個標志
    cls->setInfo(RW_LOADED); 
}
  • call_load_methods() load方法的調(diào)用

通過do-while循環(huán)中 call_class_loads()和 call_category_load()可以看出,優(yōu)先調(diào)用類的load方法翘悉,再調(diào)用分類的調(diào)用方法
load方法通過函數(shù)指針調(diào)用

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) {
            // 1.先調(diào)用類中的+load方法
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        // 2.調(diào)用分類的+load方法
        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;
}

// 調(diào)用類的load方法
static void call_class_loads(void) {
    int i;
    
    // Detach current loadable list.
    // 獲取當前l(fā)oadable_classes列表
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    
    // 從子類到父類茫打,遍歷所有類中的 +load 方法
    // 遍歷loadable_classes列表,依次執(zhí)行l(wèi)oad方法,只遍歷已添加的load方法的列表元素used
    for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        // 如果cls為空老赤,則不執(zhí)行其load方法
        if (!cls) continue; 

        // 通過函數(shù)地址直接調(diào)用load方法
        (*load_method)(cls, SEL_load);
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饼煞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子诗越,更是在濱河造成了極大的恐慌砖瞧,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚷狞,死亡現(xiàn)場離奇詭異块促,居然都是意外死亡,警方通過查閱死者的電腦和手機床未,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門竭翠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人薇搁,你說我怎么就攤上這事斋扰。” “怎么了啃洋?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵传货,是天一觀的道長。 經(jīng)常有香客問我宏娄,道長问裕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任孵坚,我火速辦了婚禮粮宛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘卖宠。我一直安慰自己巍杈,他們只是感情好,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布扛伍。 她就那樣靜靜地躺著筷畦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜒秤。 梳的紋絲不亂的頭發(fā)上汁咏,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機與錄音作媚,去河邊找鬼。 笑死帅刊,一個胖子當著我的面吹牛纸泡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼女揭,長吁一口氣:“原來是場噩夢啊……” “哼蚤假!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吧兔,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤磷仰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后境蔼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灶平,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年箍土,在試婚紗的時候發(fā)現(xiàn)自己被綠了逢享。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡吴藻,死狀恐怖瞒爬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情沟堡,我是刑警寧澤侧但,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站航罗,受9級特大地震影響俊犯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伤哺,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一燕侠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧立莉,春花似錦绢彤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至刹淌,卻和暖如春饶氏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背有勾。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工疹启, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蔼卡。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓喊崖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子荤懂,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容