iOS Objective-C底层 part1:start

作者: 破弓 | 来源:发表于2017-05-08 20:02 被阅读132次

    本文阅读的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源码
    我们的对象会经历什么

    相关文章

      网友评论

      本文标题:iOS Objective-C底层 part1:start

      本文链接:https://www.haomeiwen.com/subject/gkswzttx.html