美文网首页
ios 类的加载之类与分类搭配加载分析

ios 类的加载之类与分类搭配加载分析

作者: 瞬间完善 | 来源:发表于2020-01-21 11:06 被阅读0次

    类的加载这篇文章中,_read_images这个函数中我们分析到doneOnce,类的插入表,重映射和非懒加载类的加载过程,还有分类里面的数据是怎么加载的我们还没有分析,今天我们继续。

    1、懒加载类和非懒加载类

    _read_images中我们发现有一段代码是处理非懒加载类的

     // Realize non-lazy classes (for +load methods and static instances)
        // 实现非懒加载的类,对于load方法和静态实例变量
        for (EACH_HEADER) {
            classref_t *classlist = 
                _getObjc2NonlazyClassList(hi, &count);
            for (i = 0; i < count; i++) {
                Class cls = remapClass(classlist[i]);
             // ✅我们在这里打印一下类,看一下非懒加载类是什么时候初始化
                printf("non-lazy Class:%s\n",cls->mangledName());
    
             // 省略代码
    
             // 实现所有非懒加载的类(实例化类对象的一些信息,例如rw)
                realizeClassWithoutSwift(cls);
            }
        }
    

    我们在一个工程中创建三个类LGPersonLGStudentLGTeacher,我们执行打印一下:

    non-lazy Class:OS_dispatch_queue
    non-lazy Class:OS_dispatch_channel
    non-lazy Class:OS_dispatch_source
    non-lazy Class:OS_dispatch_mach
    non-lazy Class:OS_dispatch_queue_runloop
    non-lazy Class:OS_dispatch_semaphore
    non-lazy Class:OS_dispatch_group
    non-lazy Class:OS_dispatch_workloop
    non-lazy Class:OS_dispatch_queue_serial
    non-lazy Class:OS_dispatch_queue_concurrent
    non-lazy Class:OS_dispatch_queue_main
    non-lazy Class:OS_dispatch_queue_global
    non-lazy Class:OS_dispatch_queue_pthread_root
    non-lazy Class:OS_dispatch_queue_mgr
    non-lazy Class:OS_dispatch_queue_attr
    non-lazy Class:OS_dispatch_mach_msg
    non-lazy Class:OS_dispatch_io
    non-lazy Class:OS_dispatch_operation
    non-lazy Class:OS_dispatch_disk
    non-lazy Class:OS_voucher
    non-lazy Class:OS_dispatch_data_empty
    non-lazy Class:OS_xpc_connection
    non-lazy Class:OS_xpc_service
    non-lazy Class:OS_xpc_null
    non-lazy Class:OS_xpc_bool
    non-lazy Class:OS_xpc_double
    non-lazy Class:OS_xpc_pointer
    non-lazy Class:OS_xpc_date
    non-lazy Class:OS_xpc_data
    non-lazy Class:OS_xpc_string
    non-lazy Class:OS_xpc_uuid
    non-lazy Class:OS_xpc_fd
    non-lazy Class:OS_xpc_shmem
    non-lazy Class:OS_xpc_mach_send
    non-lazy Class:OS_xpc_array
    non-lazy Class:OS_xpc_dictionary
    non-lazy Class:OS_xpc_error
    non-lazy Class:OS_xpc_endpoint
    non-lazy Class:OS_xpc_serializer
    non-lazy Class:OS_xpc_pipe
    non-lazy Class:OS_xpc_mach_recv
    non-lazy Class:OS_xpc_bundle
    non-lazy Class:OS_xpc_service_instance
    non-lazy Class:OS_xpc_activity
    non-lazy Class:OS_xpc_file_transfer
    non-lazy Class:OS_xpc_int64
    non-lazy Class:OS_xpc_uint64
    non-lazy Class:OS_os_log
    non-lazy Class:OS_os_activity
    non-lazy Class:__IncompleteProtocol
    non-lazy Class:Protocol
    non-lazy Class:__NSUnrecognizedTaggedPointer
    non-lazy Class:NSObject
    non-lazy Class:__NSArray0
    non-lazy Class:__NSPlaceholderArray
    non-lazy Class:__NSPlaceholderSet
    non-lazy Class:NSConstantArray
    non-lazy Class:__NSDictionary0
    non-lazy Class:NSConstantDictionary
    non-lazy Class:__NSPlaceholderDate
    non-lazy Class:__NSPlaceholderTimeZone
    non-lazy Class:__NSPlaceholderOrderedSet
    non-lazy Class:NSConstantDate
    non-lazy Class:__NSPlaceholderDictionary
    non-lazy Class:NSConstantIntegerNumber
    non-lazy Class:NSConstantFloatNumber
    non-lazy Class:NSConstantDoubleNumber
    non-lazy Class:NSApplication
    non-lazy Class:NSBinder
    non-lazy Class:NSColorSpaceColor
    non-lazy Class:NSNextStepFrame
    non-lazy Class:_DKEventQuery
    non-lazy Class:LGTeacher
    non-lazy Class:LGStudent
    

    看我们控制台打印出了一堆东西,但是我们仔细看发现只打印出了LGTeacherLGStudent,为什么没有LGPerson呢?

    2020-01-20 17:20:04.051474+0800 objc-debug[20679:803821] +[LGTeacher load]
    2020-01-20 17:20:04.051925+0800 objc-debug[20679:803821] +[LGStudent load]
    

    看到这两行打印,也许大家就明白了,LGTeacherLGStudent里面实现了+ (void)load方法

    • load方法的调用是在主函数之前,系统先帮你把这个类进行初始化,不管你有没有用到,这就是非懒加载类
    • 懒加载的类就是没有实现load方法,系统没有帮你进行初始化,是需要你在用到的时候才进行初始化的
      类的加载中我们分析了非懒加载类是如何读取数据和进行rwro操作的,那么懒加载类是什么时候进行这些操作的呢?

    懒加载类操作rwro

    刚我们还说懒加载类是在我们用的时候进行初始化,也就是alloc。在消息查找流程-慢速查找中,消息会来到lookUpImpOrForward,在这个函数中有一段代码:

    //  ✅ 这里判断是否是懒加载,如果是懒加载则执行
        if (!cls->isRealized()) {
            cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
            // runtimeLock may have been dropped but is now locked again
        }
    

    我们根据这个源码往下查找:

    static Class
    realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
    {
        lock.assertLocked();
    
        if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
            // Non-Swift class. Realize it now with the lock still held.
            // fixme wrong in the future for objc subclasses of swift classes
            realizeClassWithoutSwift(cls);
            if (!leaveLocked) lock.unlock();
        } else {
            // Swift class. We need to drop locks and call the Swift
            // runtime to initialize it.
            lock.unlock();
            cls = realizeSwiftClass(cls);
            assert(cls->isRealized());    // callback must have provoked realization
            if (leaveLocked) lock.lock();
        }
    
        return cls;
    }
    

    在这里我们可以看到,懒加载经过消息转发最后又回到realizeClassWithoutSwift方法,和非懒加载类进行一模模一样样的执行。

    2、类和分类的搭配分析

    2.1、懒加载类+非懒加载分类

    上面我们分析到,非懒加载肯定会执行_read_images,_read_images里面有这样一段处理分类的代码:

     // ✅ 发现和处理所有Category
        for (EACH_HEADER) {
            // ✅ 外部循环遍历找到当前类,查找类对应的Category数组
            category_t **catlist = 
                _getObjc2CategoryList(hi, &count);
            bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    
            for (i = 0; i < count; i++) {
                // ✅ 内部循环遍历当前类的所有Category
                category_t *cat = catlist[i];
                Class cls = remapClass(cat->cls);
    
                if (!cls) {
                    // Category's target class is missing (probably weak-linked).
                    // Disavow any knowledge of this category.
                    catlist[i] = nil;
                    if (PrintConnecting) {
                        _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                     "missing weak-linked target class", 
                                     cat->name, cat);
                    }
                    continue;
                }
    
                // Process this category. 
                // First, register the category with its target class. 
                // Then, rebuild the class's method lists (etc) if 
                // the class is realized.
                //  ✅ 首先,通过其所属的类注册Category。如果这个类已经被实现,则重新构造类的方法列表。
                bool classExists = NO;
                if (cat->instanceMethods ||  cat->protocols  
                    ||  cat->instanceProperties) 
                {
                    // ✅ 将Category添加到对应Class的value中,value是Class对应的所有category数组
                    addUnattachedCategoryForClass(cat, cls, hi);
                    // ✅ 将Category的method、protocol、property添加到Class
                    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" : "");
                    }
                }
    
                // ✅ 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作
                // ✅ 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的
                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);
                    }
                }
            }
        }
    

    我们知道非懒加载类会实现load方法,在_objc_init_dyld_objc_notify_register(&map_images, load_images, unmap_image);这三个参数load_images,我们进去看看:

    void
    load_images(const char *path __unused, const struct mach_header *mh)
    {
        // Return without taking locks if there are no +load methods here.
        if (!hasLoadMethods((const headerType *)mh)) return;
    
        recursive_mutex_locker_t lock(loadMethodLock);
    
        // Discover load methods
        {
            mutex_locker_t lock2(runtimeLock);
            prepare_load_methods((const headerType *)mh);
        }
    
        // Call +load methods (without runtimeLock - re-entrant)
        call_load_methods();
    }
    

    在这里的作用域里面调用了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++) {
            schedule_class_load(remapClass(classlist[i]));
        }
    // ✅ 在这里获取非懒加载categorylist,如果存在,则进行realizeClassWithoutSwift
        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
            if (cls->isSwiftStable()) {
                _objc_fatal("Swift class extensions and categories on Swift "
                            "classes are not allowed to have +load methods");
            }
            realizeClassWithoutSwift(cls);
            assert(cls->ISA()->isRealized());
            add_category_to_loadable_list(cat);
        }
    }
    

    prepare_load_methods函数中获取到非懒加载的categoryList,如果存在非懒加载categoryList,就会在此时调用realizeClassWithoutSwift方法来初始化该class,然后开始调用methodizeClass方法,在methodizeClass方法中就会开始绑定categoryList到对应的class,代码如下:

    static void methodizeClass(Class cls)
    {
        runtimeLock.assertLocked();
    
        bool isMeta = cls->isMetaClass();
        auto rw = cls->data();
        auto ro = rw->ro;
    
        // Methodizing for the first time
        if (PrintConnecting) {
            _objc_inform("CLASS: methodizing class '%s' %s", 
                         cls->nameForLogging(), isMeta ? "(meta)" : "");
        }
    
        // Install methods and properties that the class implements itself.
        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);
        }
    
        // Root classes get bonus method implementations if they don't have 
        // them already. These apply before category replacements.
        if (cls->isRootMetaclass()) {
            // root metaclass
            addMethod(cls, SEL_initialize, (IMP)&objc_noop_imp, "", NO);
        }
    
        // Attach categories.
        // ✅ 
        category_list *cats = unattachedCategoriesForClass(cls, true /*realizing*/);
        attachCategories(cls, cats, false /*don't flush caches*/);
    
     ...省略代码
    }
    

    懒加载类本来是在发送消息的时候才开始初始化,但是因为分类是非懒加载的需要提前初始化,经过read_images 但是没有实现类,在load_iamgesprepare_load_methods提前实现了类的初始化realizeClassWithoutSwift.

    2.2、懒加载类+懒加载分类

    我们知道,懒加载是在初始化的时候发送消息,调用lookupImpOrForward方法,方法流程跟上面懒加载是一样的。

    2.3、非懒加载类+懒加载分类

    因为类为非懒加载,直接执行read_iamges方法,进行非懒加载类执行过程,而懒加载的分类直接在编译时加载在ro里面了。

    2.4、非懒加载类+非懒加载分类

    read_images方法里面处理类和分类的一段代码

     //  ✅ 首先,通过其所属的类注册Category。如果这个类已经被实现,则重新构造类的方法列表。
                bool classExists = NO;
                if (cat->instanceMethods ||  cat->protocols  
                    ||  cat->instanceProperties) 
                {
                    // ✅ 将Category添加到对应Class的value中,value是Class对应的所有category数组
                    addUnattachedCategoryForClass(cat, cls, hi);
                    // ✅ 将Category的method、protocol、property添加到Class
                    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" : "");
                    }
                }
    
                // ✅ 这块和上面逻辑一样,区别在于这块是对Meta Class做操作,而上面则是对Class做操作
                // ✅ 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的
                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);
                    }
                }
    

    我们在分类中添加的是类方法,会被存储在元类中,所以,我们只需关注:

     // ✅ 根据下面的逻辑,从代码的角度来说,是可以对原类添加Category的
                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);
                    }
                }
    

    这里会进入到remethodizeClass:

    static void remethodizeClass(Class cls)
    {
        category_list *cats;
        bool isMeta;
    
        runtimeLock.assertLocked();
    
        isMeta = cls->isMetaClass();
    
        // Re-methodizing: check for more categories
        if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
            if (PrintConnecting) {
                _objc_inform("CLASS: attaching categories to class '%s' %s", 
                             cls->nameForLogging(), isMeta ? "(meta)" : "");
            }
            
            attachCategories(cls, cats, true /*flush caches*/);        
            free(cats);
        }
    }
    

    然后添加分类attachCategories:

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

    看到这里是不是跟类的加载这篇文章中处理非懒加载类很像。

    相关文章

      网友评论

          本文标题:ios 类的加载之类与分类搭配加载分析

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