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

2020-01-21  本文已影响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方法

懒加载类操作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);
}

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

上一篇下一篇

猜你喜欢

热点阅读