澳门威利斯人_威利斯人娱乐「手机版」

来自 办公软件 2020-05-08 17:49 的文章
当前位置: 澳门威利斯人 > 办公软件 > 正文

你真的了解,Main函数之前的Load

初稿链接:

关怀仓库,及时得到改善:iOS-Source-Code-AnalyzeFollow: Draveness · Github

因为 ObjC 的 runtime 只可以在 Mac OS 下技术编写翻译,所以文章中的代码都以在 Mac OS,也正是 x86_64 布局下运营的,对于在 arm64 中运转的代码会特意表达。

随笔的标题与其说是问各位读者,不及说是问笔者自身:真的了然 load 方法么?

意识到load方法是因为最近读书Method Swilzzing时发掘与别的的系统方法不一样。 那时候开创了多少个UIViewController的catagory同仁一视写了load方法。

整体运维时起初化时 _objc_init 注册的回调

load 作为 Objective-C 中的叁个格局,与别的方法有极大的两样。它只是二个在全方位文件被加载到运营时,在 main 函数调用早前被 ObjC 运维时调用的钩子方法。当中首要字有这么多少个:

 

dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/not batch/, &load_images);

  • 文本刚加载
  • main 函数早先
  • 钩子方法

这篇文章中指出:

每当有新的镜像加载之后,都会实施 load_images 方法举办回调,并传播最新镜像的新闻列表 infoList:

自家在翻阅 ObjC 源代码在此以前,曾经一度以为自个儿对 load 方法的法力拾叁分领悟,直到看了源代码中的落成,才知晓在此以前的感觉,只是本身的感到罢了。

load 作为 Objective-C 中的一个艺术,与别的方式有十分的大的两样。只是二个在总体文件被加载到运维时,在 main 函数调用早先被 ObjC 运行时调用的钩方法。在那之中重要字有这么多少个:

那就是说,什么是镜像?

那篇文章会假如你精通:

  • 文件刚加载

  • main 函数在此以前

  • 钩子方法

镜像并非二个 Objective-C 的代码文件,它应有是三个 target 的编写翻译成品。这里注意了 这里用词 编写翻译付加物,金科玉律的想到了 动态库,上面包车型客车打字与印刷镜像的音信,镜像的门径,我们看出了动态库

  • 使用过 load 方法
  • 知道 load 方法的调用顺序

 

const char *  load_images(enum dyld_image_states state, uint32_t infoCount,

在这里篇小说中并不会用大篇幅介绍 load 方法的成效其实也没几个作用,关切点首要在以下多个问题上:

针对提问举办重新整建学习

const struct dyld_image_info infoList[])

  • load 方法是什么样被调用的
  • load 方法为何会有这种调用顺序

Q1:load 方法是何等被调用的?

{

先是来通过 load 方法的调用栈,分析一下它毕竟是如何被调用的。

A1:当 Objective-C 运转时早先化的时候,会透过 dyld_register_image_state_change_handler 在历次有新的镜像到场运转时的时候,举行回调。试行 load_images 将拥有包括load 方法的文书参与列表 loadable_classes ,然后从那些列表中找到呼应的 load 方法的兑现,调用 load 方法。

        bool found;

上面是程序的整个代码:

Q2:load 方法会有为大家所知的这种调用顺序?**

        // Return without taking locks if there are no load methods here. 

// main.m#import <Foundation/Foundation.h>@interface XXObject : NSObject @end@implementation XXObject  load { NSLog(@"XXObject load");}@endint main(int argc, const char * argv[]) { @autoreleasepool { } return 0;}

       法则一:父类先于子类调用 

        found = false;

代码总共只兑现了二个 XXObject load 方法,主函数中也尚未其余的事物:

       法规二:类先于分类调用

        for (uint32_t i = 0; i < infoCount; i ) {

图片 1objc-load-print-load

A2:分析 schedule_class_load 和 call_load_methods 可得。

     //Quick scan Load     不论是ClassList照旧CategoryList,真就回去,甘休遍历

虽说在主函数中怎么样情势都未曾调用,不过运维之后,仍然打字与印刷了 XXObject load 字符串,也正是说调用了 load 方法。

//  在系统内核做好程序准备工作之后,交由 dyld(是the dynamic link editor 的缩写,它是苹果的动态链接器) 负责余下的工作。 

//  1、首先在整个运行时初始化时 _objc_init 注册的回调
dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);

//  2、每当有新的镜像加载到 runtime 时,都会执行 load_images 方法进行回调,并传入最新镜像的信息列表 infoList
const char * load_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]) {
    bool found;
    found = false;
    for (uint32_t i = 0; i < infoCount; i  ) {
        if (hasLoadMethods((const headerType *)infoList[i].imageLoadAddress)) {
        //  3、如果在扫描镜像的过程中发现了   load 符号 
            found = true;
            break;
        }
    }
    if (!found) return nil;
    recursive_mutex_locker_t lock(loadMethodLock);
    {
        rwlock_writer_t lock2(runtimeLock);
        //  4、会进入 load_images_nolock 来查找 load 方法
        found = load_images_nolock(state, infoCount, infoList);
    }
    if (found) {
        //  7、在将镜像加载到运行时、对 load 方法的准备就绪之后,执行 call_load_methods 开始调用 load 方法 
        call_load_methods();
    }
    return nil;
}

//  4、调用 load_image_nolock 
bool load_images_nolock(enum dyld_image_states state,uint32_t infoCount, const struct dyld_image_info infoList[] {
    bool found = NO;
    uint32_t i;
    i = infoCount;
    while (i--) {
        const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress;
        if (!hasLoadMethods(mhdr)) continue;
        //  5、调用 prepare_load_methods 对 load 方法的调用进行准备
        prepare_load_methods(mhdr);
        found = YES;
    }
    return found;
}

//  5、调用 prepare_load_methods(将需要调用 load 方法的类添加到一个列表中)
void prepare_load_methods(const headerType *mhdr) {
    size_t count, i;
    runtimeLock.assertWriting();
    /* 通过 _getObjc2NonlazyClassList 获取所有的类的列表 */
    classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i  ) {
        //  6、调用 schedule_class_load 递归地安排当前类和没有调用 load 的父类进入列表
        /* 通过 remapClass 获取类对应的指针 */ 
        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;  // category for ignored weak-linked class
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

//  6、调用 schedule_class_load (红色代码保证了父类在子类前面调用 load 方法)
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_loadabel_list 将当前类也加入进去 */
    add_class_to_loadable_list(cls);                      ---------------------------------------》得出规则一
    cls->setInfo(RW_LOADED); 
}

//  其中 add_class_to_loadable_list 添加到待加载的类的列表
void add_class_to_loadable_list(Class cls) {
    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  ;
}

//  其中 add_category_to_loadable_list 添加到待加载的分类的列表
void add_category_to_loadable_list(Category cat) {
    IMP method;
    loadMethodLock.assertLocked();
    method = _category_getLoadMethod(cat);
    if (!method) return;
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2   16;
        loadable_categories = (struct loadable_category *)
            realloc(loadable_categories,
                              loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }
    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used  ;
}

//  7、调用 call_load_methods
void call_load_methods(void) {
    ...
    do {
        while (loadable_classes_used > 0) {
            call_class_loads();
        }
        more_categories = call_category_loads();            --------------------------------》得出规则二
    } while (loadable_classes_used > 0  ||  more_categories);
    ...
}

//  其中call_class_loads 会从一个待加载的类列表 loadable_classes 中寻找对应的类,然后找到 @selector(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;
    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;
        (*load_method)(cls, SEL_load); // 真正调用  [XXObject load] 方法
    }
    if (classes) free(classes);
}

              if (hasLoadMethods((const headerType *)infoList[i].imageLoadAddress)) {

选取标记断点

应用 Xcode 增加四个标识断点 [XXObject load]

小心这里 [ 之间平昔不空格

图片 2objc-load-symbolic-breakpoint

干什么要加八个标识断点呢?因为如此看起来相比较高档。

再也运路程序。这个时候,代码会停在 NSLog(@"XXObject load"); 这一行的得以达成上:

图片 3objc-load-break-after-add-breakpoint

左边的调用栈很明亮的告诉我们,哪些方法被调用了:

0  [XXObject load]1 call_class_loads()2 call_load_methods3 load_images4 dyld::notifySingle(dyld_image_states, ImageLoader const*)11 _dyld_start

dyld 是 the dynamic link editor 的缩写,它是苹果的动态链接器

在系统基本做好程序筹算专门的学问未来,交由 dyld 负担余下的办事。本文不会对其展开讲授

每当有新的镜像加载之后,都会实行 3 load_images 方法开展回调,这里的回调是在方方面面运转时伊始化时 _objc_init 注册的(会在之后的篇章中现实介绍):

dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);

有新的镜像被加载到 runtime 时,调用 load_images 方法,并传播最新镜像的音讯列表 infoList

const char *load_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]){ bool found; found = false; for (uint32_t i = 0; i < infoCount; i  ) { if (hasLoadMethods((const headerType *)infoList[i].imageLoadAddress)) { found = true; break; } } if  return nil; recursive_mutex_locker_t lock(loadMethodLock); { rwlock_writer_t lock2(runtimeLock); found = load_images_nolock(state, infoCount, infoList); } if  { call_load_methods(); } return nil;}

补充:

                       found = true;

何以是镜像

此处就能凌驾一个主题材料:镜像到底是何等,我们用七个断点打印出具备加载的镜像:

图片 4objc-load-print-image-info

从决定台出口的结果大概正是这么的,大家能够看来镜像并非三个 Objective-C 的代码文件,它应当是三个 target 的编写翻译付加物。

...(const dyld_image_info) $52 = { imageLoadAddress = 0x00007fff8a144000 imageFilePath = 0x00007fff8a144168 "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices" imageFileModDate = 1452737802}(const dyld_image_info) $53 = { imageLoadAddress = 0x00007fff946d9000 imageFilePath = 0x00007fff946d9480 "/usr/lib/liblangid.dylib" imageFileModDate = 1452737618}(const dyld_image_info) $54 = { imageLoadAddress = 0x00007fff88016000 imageFilePath = 0x00007fff88016d40 "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation" imageFileModDate = 1452737917}(const dyld_image_info) $55 = { imageLoadAddress = 0x0000000100000000 imageFilePath = 0x00007fff5fbff8f0 "/Users/apple/Library/Developer/Xcode/DerivedData/objc-dibgivkseuawonexgbqssmdszazo/Build/Products/Debug/debug-objc" imageFileModDate = 0}

那之中有无数的动态链接库,还会有局地苹果为大家提供的框架,比如Foundation、 CoreServices 等等,都以在这里个 load_images 中加载进来的,而这几个 imageFilePath 都以应和的二进制文件的地址。

唯独如果进入最上面包车型大巴那一个目录,会意识它是贰个可实践文件,它的周转结果与 Xcode 中的运维结果相同:

图片 5objc-load-image-binary

1)什么是镜像?

                        break;

准备 load 方法

咱俩再一次归来 load_images 方法,假若在围观镜像的长河中开掘了 load 符号:

for (uint32_t i = 0; i < infoCount; i  ) { if (hasLoadMethods((const headerType *)infoList[i].imageLoadAddress)) { found = true; break; }}

就能步入 load_images_nolock 来查找 load 方法:

bool load_images_nolock(enum dyld_image_states state,uint32_t infoCount, const struct dyld_image_info infoList[]){ bool found = NO; uint32_t i; i = infoCount; while  { const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress; if (!hasLoadMethods continue; prepare_load_methods; found = YES; } return found;}

调用 prepare_load_methodsload 方法的调用举办盘算(将需求调用 load 方法的类加多到五个列表中,前面包车型大巴小节中会介绍):

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  ) { 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  continue; // category for ignored weak-linked class realizeClass; assert(cls->ISA()->isRealized; add_category_to_loadable_list; }}

通过 _getObjc2NonlazyClassList 获取具有的类的列表之后,会透过 remapClass 获取类对应的指针,然后调用 schedule_class_load 递归地陈设当前类和还没调用 load 父类跻身列表。

static void schedule_class_load(Class cls){ if  return; assert(cls->isRealized; if (cls->data()->flags & RW_LOADED) return; schedule_class_load(cls->superclass); add_class_to_loadable_list; cls->setInfo(RW_LOADED); }

在执行 add_class_to_loadable_list 将日前类参加加载列表在此以前,会先把父类参加待加载的列表,保险父类在子类前调用 load 方法。

 

                }

调用 load 方法

在将镜像加载到运营时、对 load 方法的备选妥帖之后,实施 call_load_methods,最早调用 load 方法:

void call_load_methods{ ... do { while (loadable_classes_used > 0) { call_class_loads(); } more_categories = call_category_loads(); } while (loadable_classes_used > 0 || more_categories); ...}

措施的调用流程大致是如此的:

图片 6objc-load-diagra

其中 call_class_loads 会从一个待加载的类列表 loadable_classes 中寻觅对应的类,然后找到 @selector 的兑现并试行。

static void call_class_loads{ int i; struct loadable_class *classes = loadable_classes; int used = loadable_classes_used; loadable_classes = nil; loadable_classes_allocated = 0; loadable_classes_used = 0; for (i = 0; i < used; i  ) { Class cls = classes[i].cls; load_method_t load_method = (load_method_t)classes[i].method; if  continue; (*load_method)(cls, SEL_load); } if  free;}

这行 (*load_method)(cls, SEL_load) 代码就可以调用 [XXObject load] 方法。

大家会在底下介绍 loadable_classes 列表是什么管理的。

到现在,我们应对了第一个难题:

Q:load 方法是怎么被调用的?

A:当 Objective-C 运转时开首化的时候,会由此 dyld_register_image_state_change_handler 在历次有新的镜像到场运行时的时候,实行回调。施行 load_images 将具有包蕴 load 方法的文件到场列表 loadable_classes ,然后从那么些列表中找到相应的 load 方法的兑现,调用 load 方法。

ObjC 对于加载的管理,主要使用了多个列表,分别是 loadable_classesloadable_categories

措施的调用进程也分为三个部分,筹划 load 方法和调用 load 方法,作者更感到那三个部分可比像分娩者与客户:

图片 7objc-load-producer-consumer-diagra

add_class_to_loadable_list 方法顶住将类出席 loadable_classes 集合,而 call_class_loads 担当花费集结中的成分。

而对此分类的话,其模型也是雷同的,只可是使用了另二个列表 loadable_categories

2)由上得出 

            }

“生产” loadable_class

在调用 load_images -> load_images_nolock -> prepare_load_methods -> schedule_class_load -> add_class_to_loadable_list 的时候会将未加载的类增多到 loadable_classes 数组中:

void add_class_to_loadable_list(Class cls){ IMP method; loadMethodLock.assertLocked(); method = cls->getLoadMethod(); if  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  ;}

方法刚被调用时:

  1. 会从 class 中获取 load 方法: method = cls->getLoadMethod();
  2. 认清当前 loadable_classes 那一个数组是或不是曾经被全部攻下了:loadable_classes_used == loadable_classes_allocated
  3. 在当下数组的基础上扩展数组的高低:realloc
  4. 把传播的 class 以致相应的点子的兑现加到列表中

别的一个用于保存分类的列表 loadable_categories 也可以有叁个近乎的艺术 add_category_to_loadable_list

void add_category_to_loadable_list(Category cat){ IMP method; loadMethodLock.assertLocked(); method = _category_getLoadMethod; if  return; if (loadable_categories_used == loadable_categories_allocated) { loadable_categories_allocated = loadable_categories_allocated*2   16; loadable_categories = (struct loadable_category *) realloc(loadable_categories, loadable_categories_allocated * sizeof(struct loadable_category)); } loadable_categories[loadable_categories_used].cat = cat; loadable_categories[loadable_categories_used].method = method; loadable_categories_used  ;}

得以达成大致与 add_class_to_loadable_list 完全相仿。

到这里大家完结了对 loadable_classes 以及 loadable_categories 的提供,上面会先河消耗列表中的成分。

  load_images -> load_images_nolock -> prepare_load_methods -> schedule_class_load -> add_class_to_loadable_list

           if (!found) return nil;

“消费” loadable_class

调用 load 方法的进度正是“花费” loadable_classes 的过程,load_images -> call_load_methods -> call_class_loads 会从 loadable_classes 中收取对应类和章程,实施 load

void call_load_methods{ static bool loading = NO; bool more_categories; loadMethodLock.assertLocked(); if  return; loading = YES; void *pool = objc_autoreleasePoolPush(); do { while (loadable_classes_used > 0) { call_class_loads(); } more_categories = call_category_loads(); } while (loadable_classes_used > 0 || more_categories); objc_autoreleasePoolPop; loading = NO;}

上述方式对负有在 loadable_classes 以及 loadable_categories 中的类以致分类实行 load 方法。

do { while (loadable_classes_used > 0) { call_class_loads(); } more_categories = call_category_loads();} while (loadable_classes_used > 0 || more_categories);

调用顺序如下:

  1. 不停调用类的 load 方法,直到 loadable_classes 为空
  2. 调用一次 call_category_loads 加载分类
  3. 如果有 loadable_classes 恐怕越来越多的归类,继续调用 load 方法

相比较之下于类 load 方法的调用,分类中 load 方法的调用就多少复杂了:

static bool call_category_loads{ int i, shift; bool new_categories_added = NO; // 1. 获取当前可以加载的分类列表 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; 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  continue; cls = _category_getClass; if (cls && cls->isLoadable { // 2. 如果当前类是可加载的 `cls && cls->isLoadable()` 就会调用分类的 load 方法 (*load_method)(cls, SEL_load); cats[i].cat = nil; } } // 3. 将所有加载过的分类移除 `loadable_categories` 列表 shift = 0; for (i = 0; i < used; i  ) { if (cats[i].cat) { cats[i-shift] = cats[i]; } else { shift  ; } } used -= shift; // 4. 为 `loadable_categories` 重新分配内存,并重新设置它的值 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  { loadable_categories = cats; loadable_categories_used = used; loadable_categories_allocated = allocated; } else { if  free; loadable_categories = nil; loadable_categories_used = 0; loadable_categories_allocated = 0; } return new_categories_added;}

以此办法有个别长,我们来分步解释方式的效应:

  1. 赢得当前得以加载的归类列表
  2. 一经当前类是可加载的 cls && cls->isLoadable() 就能够调用分类的 load 方法
  3. 将兼具加载过的分类移除 loadable_categories 列表
  4. loadable_categories 重新分配内存,同等对待复设置它的值

你过去大概会据他们说过,对于 load 方法的调用顺序有两条准则:

  1. 父类先于子类调用
  2. 类先于分类调用

这种场馆是特别相符大家的直觉的,大家来深入分析一下这种现象现身的因由。

首先条法则是由于 schedule_class_load 有如下的落实:

static void schedule_class_load(Class cls){ if  return; assert(cls->isRealized; if (cls->data()->flags & RW_LOADED) return; schedule_class_load(cls->superclass); add_class_to_loadable_list; cls->setInfo(RW_LOADED); }

这里经过那行代码 schedule_class_load(cls->superclass) 总是能够保证未有调用 load 方法的父类先于子类参与 loadable_classes 数组,进而确认保障其调用顺序的科学。

类与分类中 load 方法的调用顺序主要在 call_load_methods 中实现:

do { while (loadable_classes_used > 0) { call_class_loads(); } more_categories = call_category_loads();} while (loadable_classes_used > 0 || more_categories);

上面的 do while 语句能够在必然水准上保障,类的 load 方法会先于分类调用。然而此地无法完全保证调用顺序的对的。

如果分拣的镜像在类的镜像早先加载到运营时,下面的代码就无法保险顺序的正确性了,所以,大家还需求在 call_category_loads 中剖断类是还是不是曾经加载到内存中(调用 load 方法):

if (cls && cls->isLoadable { (*load_method)(cls, SEL_load); cats[i].cat = nil;}

此地,检查了类是或不是存在何况是还是不是足以加载,假使都为真,那么就足以调用分类的 load 方法了。

load 能够说大家在平凡支出中得以接触到的调用时间最靠前的艺术,在主函数运转以前,load 方法就能够调用。

出于它的调用不是惰性的,且其只会在前后相继调用时期调用一遍,最最关键的是,如若在类与分类中都贯彻了 load 方法,它们都会被调用,不像任何的在分拣中落到实处的法子会被遮掩,这就使 load 方法成为了章程调护医疗的绝佳时机。

而是出于 load 方法的运营时刻太早,所以这里大概不是贰个地道的条件,因为有个别类或然须求在在其余类此前加载,但是那是我们力不能及确认保证的。不过在这里个时间点,全体的 framework 都曾经加载到了运转时中,所以调用 framework 中的方法都以安全的。

  • NSObject load and initialize - What do they do?
  • Method Swizzling
  • Objective-C Class Loading and Initialization

关怀仓库,及时取得更新:iOS-Source-Code-AnalyzeFollow: Draveness · Github

的调用进程。ObjC 对于加载的治本注重接受了七个列表,分别是 loadable_classes 和 loadable_categories。其中的add_class_to_loadable_list就是将未加载的类增加到 loadable_classes 数组中,add_category_to_loadable_list完毕大约一致

           recursive_mutex_locker_t lock(loadMethodLock);

 

            // Discover load methods

3)比较于类 load 方法的调用,分类中 load 方法的调用就有一点点复杂了

{

static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    // 1. 获取当前可以加载的分类列表
    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;
    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()) {
            // 2. 如果当前类是可加载的 `cls  &&  cls->isLoadable()` 就会调用分类的 load 方法
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }
    // 3. 将所有加载过的分类移除 `loadable_categories` 列表
    shift = 0;
    for (i = 0; i < used; i  ) {
        if (cats[i].cat) {
            cats[i-shift] = cats[i];
        } else {
            shift  ;
        }
    }
    used -= shift;
    // 4. 为 `loadable_categories` 重新分配内存,并重新设置它的值
    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;
}

      rwlock_writer_t lock2(runtimeLock);  

 

       found = load_images_nolock(state, infoCount, infoList);

 

}

 

      //上边落成了加多类及分类到loadable_classes及loadable_categories,然后开端进行格局

             // Call load methods

            if (found) {

             call_load_methods();

            }

      return nil;

}

bool load_images_nolock(enum dyld_image_states state,uint32_t infoCount,

const struct dyld_image_info infoList[])

{

       bool found = NO;

       uint32_t i;

        i = infoCount;

        while (i--) {

                const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress;

             if (!hasLoadMethods(mhdr)) continue;

//对infoList遍历,若无Load方法,继续循环,有就对load 方法的调用举办筹算

                 prepare_load_methods(mhdr);

                 found = YES;

       }

       return found;

}

void prepare_load_methods(const headerType *mhdr)

{

      size_t count, i;

      runtimeLock.assertWriting();

      //通过 _getObjc2NonlazyClassList 获取具备的类的列表之后

      classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);

// 通过 remapClass 获取类对应的指针,调用 schedule_class_load 递归地布署当前类和未有调用 load 父类步向列表。

        for (i = 0; i < count; i ) {

          schedule_class_load(remapClass(classlist[i]));

        }

     // 这里,类与其load方法增添到loadable_classes数组

    // 然后增添分类到相应数组

     category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);

     for (i = 0; i < count; i ) {

本文由澳门威利斯人发布于办公软件,转载请注明出处:你真的了解,Main函数之前的Load

关键词: 澳门威利斯人 日记本 方法 你真 load