iOS Objective-C底层 part1:start

本文阅读的objc源码

打个符号断点:_objc_init,可以看到以下调用栈

start.png

1. dyld 链接动态库

app在被打开的时候所依赖的动态库会被加载到程序中.
dyld(the dynamic link editor)
这样一部手机所有app都共用系统的动态库,这样做大大的减小了可执行文件(ipa)大小.
将模拟器路径上的PGRuntimeTrain.app ShowInFinder,然后:

otool -L PGRuntimeTrain.app/PGRuntimeTrain
PGRuntimeTrain.app/PGRuntimeTrain:
    /System/Library/Frameworks/Foundation.framework/Foundation (compatibility version 300.0.0, current version 1349.54.0)
    /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
    /usr/lib/libSystem.dylib (compatibility version 1.0.0, current version 1238.50.2)
    /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (compatibility version 150.0.0, current version 1349.55.0)
    /System/Library/Frameworks/UIKit.framework/UIKit (compatibility version 1.0.0, current version 3600.7.47)
Foundation.framework
CoreFoundation.framework
UIKit.framework
libobjc.A.dylib
├──objc
└──runtime
libSystem.dylib
├── libdispatch//GCD
├── libsystem_c//C语言库
├── libsystem_blocks//Block
└── libcommonCrypto//加密库,比如常用的 md5 函数

在做一些"逆"的工作时在这个环节可以加入自己写的.dylib,功力不够就不细说了

2. ImageLoader 加载镜像文件

动态库加载完成后就该加载我们自己的编写的代码编译成的二进制文件了,就是ImageLoaderXXXXXX系列方法.这些image内就编译着我们自己写的符号、代码等.

3. _objc_init runtime初始化

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
   //读取镜像前,基本环境的初始化
    environ_init();
    tls_init();
    static_init();
    lock_init();
    exception_init();

    _dyld_objc_notify_register(&map_2_images, load_images, unmap_image);
}
3.1 map_2_images
map_2_images.png
map_2_images
└─map_images_nolock
  └─_read_images
  • map_images_nolock

map_images_nolock内的已经完成了:
header_info *hList[mhCount];==>类信息读取到header_info的链表数组
preopt_init==>优化共享缓存的初始化
sel_init==>初始化方法列表
arr_init==>初始化自动释放池+散列表

  • _read_image

_read_image方法内所有类的信息全放在header_info的链表数组之中,以该链表数组出发:

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
"IMAGE TIMES: first time tasks”—>support indexed_isa
"IMAGE TIMES: discover classes”—>_getObjc2ClassList
"IMAGE TIMES: remap classes”—>_getObjc2ClassRefs
"IMAGE TIMES: fix up selector references”—>_getObjc2SelectorRefs
"IMAGE TIMES: fix up objc_msgSend_fixup”—>_getObjc2MessageRefs
"IMAGE TIMES: discover protocols”—>_getObjc2ProtocolList
"IMAGE TIMES: fix up @protocol references”—>_getObjc2ProtocolRefs
"IMAGE TIMES: realize non-lazy classes”—>_getObjc2NonlazyClassList
"IMAGE TIMES: realize future classes”—>resolvedFutureClasses
"IMAGE TIMES: discover categories”—>_getObjc2CategoryList
  • 1.确认是否支持indexed_isa

  • 2.读取所有类信息

classref_t *classlist = _getObjc2ClassList(hi, &count);
for (i = 0; i < count; i++) {
    Class cls = (Class)classlist[i];
    Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
    
    if (newCls != cls  &&  newCls) {
        // Class was moved but not deleted. Currently this occurs
        // only when the new class resolved a future class.
        // Non-lazily realize the class below.
        resolvedFutureClasses = (Class *)
        realloc(resolvedFutureClasses,
                (resolvedFutureClassCount+1) * sizeof(Class));
        resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
    }
}

当调用readClass方法返回的Class newCls与传入的cls不一样时,则会将Class newCls加了数组Class *resolvedFutureClasses,方便后面对类做实现(第6步).

  • 3.读取所有方法信息

  • 4.读取所有协议信息

  • 5.实现所有的non-lazy classes

注意第2步:读取所有类信息内用的是:

classref_t *classlist = _getObjc2ClassList(hi, &count);

而现在用的是:

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

实现了+load的类就是non-lazy classes,没有实现+load的类就是lazy classes

realizeClass//cls->ro到cls->rw->ro
└─methodizeClass//向cls->rw内填充方法列表,属性列表,协议列表 
  └─attachCategories//读取类对应分类的内容

5.1 realizeClass
realizeClass除了实现本类外,还实现本类的父类和元类.在此之前,cls->data()指向的是ro,在realizeClass内,完成了cls->rocls->rw->ro的转换(字面也能看出来:ro->readonly,rw->readwrite,之后rw内就可以写入内容了)

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);
}

5.2 methodizeClass
cls->rw内填充方法列表,属性列表,协议列表

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);
}

5.3 attachCategories
读取类对应分类内的方法列表,属性列表,协议列表,填入cls->rw

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);
}
  • 6.resolvedFutureClasses
if (resolvedFutureClasses) {
    for (i = 0; i < resolvedFutureClassCount; i++) {
        realizeClass(resolvedFutureClasses[i]);
        resolvedFutureClasses[i]->setInstancesRequireRawIsa(false/*inherited*/);
    }
    free(resolvedFutureClasses);
}

将第2步readClass中产生的Class *resolvedFutureClasses内的类全部初始化.

  • 7.读取所有分类信息

可能你会说不是在attachCategories方法内读取过类对应分类的消息然后填入cls->rw吗?
以上已经说过了realizeClass初始化的是non-lazy class(实现了+load方法的类),所以还是有许多lazy class没有初始化.

category_t **catlist = _getObjc2CategoryList(hi, &count);
Class cls = remapClass(cat->cls);

读取分类列表,由单个分类(cat)反拿所属类(cls),

bool classExists = NO;
if (cat->instanceMethods ||  cat->protocols
    ||  cat->instanceProperties)
{
    addUnattachedCategoryForClass(cat, cls, hi);
    if (cls->isRealized()) {
        remethodizeClass(cls);
        classExists = YES;
    }
    if (PrintConnecting) {
        _objc_inform("CLASS: found category -%s(%s) %s",
                     cls->nameForLogging(), cat->name,
                     classExists ? "on existing class" : "");
    }
}

if (cat->classMethods  ||  cat->protocols
    ||  (hasClassProperties && cat->_classProperties))
{
    addUnattachedCategoryForClass(cat, cls->ISA(), hi);
    if (cls->ISA()->isRealized()) {
        remethodizeClass(cls->ISA());
    }
    if (PrintConnecting) {
        _objc_inform("CLASS: found category +%s(%s)",
                     cls->nameForLogging(), cat->name);
    }
}

7.1 addUnattachedCategoryForClass绑定类与分类的关系.
7.2 如果满足(cat->instanceMethods||cat->protocols||cat->instanceProperties),则看类有没有被初始化cls->isRealized(),初始化了则调用remethodizeClass.
remethodizeClassmethodizeClass简单了很多,因为调用remethodizeClass方法的情况下,类肯定是已经被初始化了.当然remethodizeClass最后也调用了attachCategories.

cat->instanceMethods
cat->protocols  
cat->instanceProperties

以上三项对应类;

cat->classMethods
cat->protocols  
cat->_classProperties

以上三项则对应类的元类;

当然调用addUnattachedCategoryForClassremethodizeClass的逻辑,类与元类是一样的.

存疑: realizeClass->methodizeClass->attachCategories
discover categories->remethodizeClass->attachCategories


two way

way1:初始化所有非懒加载类->填充3大列表->取出分类再填充3大列表
way2:拿出所有分类->反拿分类的类->类已经初始化->取出分类再填充3大列表
好像做了重复的工作,存疑

3.2 load_images
load_images.png
void load_images(const char *path __unused, const struct mach_header *mh)
{
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        rwlock_writer_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

load_images的功能会比map_2_images简明很多,就是调用所有实现+load方法的类与分类.

    1. prepare_load_methods(生产)
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 (!cls) continue;  // category for ignored weak-linked class
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}
  • 生产类

注意这边获取non_lazy class列表的方法与先前讲过获取方法已经不一样了:

classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
extern classref_t *_getObjc2NonlazyClassList(const header_info *hi, size_t *count);
classref_t *classlist = _getObjc2NonlazyClassList(hi, &count);
extern classref_t *_getObjc2NonlazyClassList(const headerType *mhdr, size_t *count);

传入的值由header_info变成了headerType.

在获取所有non_lazy class后,调用schedule_class_load,将
non_lazy class加入静态结构体数组static struct loadable_class *loadable_classes;

struct loadable_class {
    Class cls;  // may be nil
    IMP method;
};
//cls->对应类
//method->+load方法的地址

schedule_class_load内有一个特殊处理:就是用类的父类递归调用schedule_class_load,这样数组loadable_classes内父类就排在前边,父类的+load自然也就先调用.

static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = cls->getLoadMethod();
    if (!method) return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    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++;
}
  • 生产分类

处理好实现了+load的类后就是要处理实现了+load的分类了.

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);
}
void add_category_to_loadable_list(Category cat)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = _category_getLoadMethod(cat);

    // Don't bother if cat has no +load method
    if (!method) return;

    if (PrintLoading) {
        _objc_inform("LOAD: category '%s(%s)' scheduled for +load", 
                     _category_getClassName(cat), _category_getName(cat));
    }
    
    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++;
}

获取所有实现+load的分类:

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

加入静态结构体数组static struct loadable_category *loadable_categories;

struct loadable_category {
    Category cat;  // may be nil
    IMP method;
};
//cat->对应分类
//method->+load方法的地址
    1. call_load_methods(消费)

在做完前面的生产准备工作之后,就是要消费准备好的类和分类了.

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();
    printf("call_load_methods up \n");
    do {
        printf("call_load_methods in \n");
        // 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);
    printf("call_load_methods down \n");
    objc_autoreleasePoolPop(pool);

    loading = NO;
}

顺序是先类后分类,所以+load方法的调用顺序可以总结为父类->类->分类.

  • 消费类

call_class_loads遍历准备好的loadable_classes内的每一个struct loadable_class,调用(*load_method)(cls, SEL_load);

  • 消费分类

call_category_loads遍历准备好的loadable_categories内的每一个struct loadable_category,调用(*load_method)(cls, SEL_load);

3.3 unmap_image

跑了源码没有调用过unmap_image,想来是针对一些读取镜像失败的特殊情况的

在runtime初始化完成之际可以看到只有non_lazy class加载好了,其他的类还没加载到内存中,那这些类有事什么时候才加载那内存中呢?这些类会在程序第一次用到这些类的时候才初始化到内存中,类的+initialize方法也就是在此时被调用的.

所有流程可以合并以下流程图:


_objc_init.png

文章参考:
objc源码
我们的对象会经历什么

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容