iOS底层原理之类的加载探究

1 前言

咱们从iOS底层原理之—dyld与objc的关联中知道dyld关联objc时,经过_read_images函数中的readClass函数来读取类的信息并将类关联起来,咱们本文主要来探讨类的加载。markdown

2 先看_read_images函数

因为咱们探究的是类的加载,且在iOS底层原理之—dyld与objc的关联中咱们已经主要分析了下_read_images函数的做用,所以咱们主要看_read_images函数中有关的部分代码,上代码app

// Discover classes. Fix up unresolved future classes. Mark bundle classes.
    bool hasDyldRoots = dyld_shared_cache_some_image_overridden();

    for (EACH_HEADER) {
        if (! mustReadClasses(hi, hasDyldRoots)) {
            // Image is sufficiently optimized that we need not call readClass()
            continue;
        }

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

        bool headerIsBundle = hi->isBundle();
        bool headerIsPreoptimized = hi->hasPreoptimizedClasses();

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

    ts.log("IMAGE TIMES: discover classes");
复制代码

2.1 分析调用readClass部分代码

2.1.1 没有调用readClass以前cls分析

在没有调用readClass时,咱们读取cls信息从上图中咱们发现此时cls只是一串地址函数

2.1.2 调用readClass以后cls分析

调用readClass以后,咱们读取cls信息从上图中咱们得知cls已经由一串地址变成了LGPerson类。所以咱们得出必定是readClsss函数作了什么,咱们来分析下readClsss函数。post

3 readClsss函数分析

3.1 源码

Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName  = cls->mangledName();
    const char *LGPersonName = "LGPerson";
    if (strcmp(mangledName, LGPersonName) == 0) {
        auto kc_ro = (const class_ro_t *)cls->data();
        printf("readClass: 这个是我要研究的 %s \n",LGPersonName);
    }
    if (missingWeakSuperclass(cls)) {
        // No superclass (probably weak-linked). 
        // Disavow any knowledge of this subclass.
        if (PrintConnecting) {
            _objc_inform("CLASS: IGNORING class '%s' with "
                         "missing weak-linked superclass", 
                         cls->nameForLogging());
        }
        addRemappedClass(cls, nil);
        cls->superclass = nil;
        return nil;
    }
    cls->fixupBackwardDeployingStableSwift();
    Class replacing = nil;
    if (Class newCls = popFutureNamedClass(mangledName)) {
        // This name was previously allocated as a future class.
        // Copy objc_class to future class's struct.
        // Preserve future's rw data block.
        if (newCls->isAnySwift()) {
            _objc_fatal("Can't complete future class request for '%s' "
                        "because the real class is too big.", 
                        cls->nameForLogging());
        }
        class_rw_t *rw = newCls->data();
        const class_ro_t *old_ro = rw->ro();
        memcpy(newCls, cls, sizeof(objc_class));
        rw->set_ro((class_ro_t *)newCls->data());
        newCls->setData(rw);
        freeIfMutable((char *)old_ro->name);
        free((void *)old_ro);
        
        addRemappedClass(cls, newCls);
        
        replacing = cls;
        cls = newCls;
    }
    
    if (headerIsPreoptimized  &&  !replacing) {
        // class list built in shared cache
        // fixme strict assert doesn't work because of duplicates
        // ASSERT(cls == getClass(name));
        ASSERT(getClassExceptSomeSwift(mangledName));
    } else {
        addNamedClass(cls, mangledName, replacing);
        addClassTableEntry(cls);
    }

    // for future reference: shared cache never contains MH_BUNDLEs
    if (headerIsBundle) {
        cls->data()->flags |= RO_FROM_BUNDLE;
        cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
    }
    return cls;
}
复制代码

源码中为了更好的区别出LGPerson,特地添加了判断LGPerson类型的代码ui

3.2 代码跟踪

经过代码调试,最终执行了addNamedClsss函数在执行addNamedClsss函数以前打印cls依然是一串地址。当执行完addNamedClsss函数时,打印cls发现指向了LGPerson所以咱们得出addNamedClsss对cls进行了指向操做。this

3.3 addNamedClsss函数

4 懒加载类与非懒加载类

4.1 定义

  • 懒加载类:推迟到第一次消息发送的时候才加载的类称为懒加载类
  • 非懒加载类:在map_images函数执行_read_images函数时就加载的类,称为非懒加载类
  • 通俗理解就是当某个类实现了+load函数,则就是非懒加载类,没有实现就是懒加载类

4.2 懒加载类分析

对于LGPerson类不实现+load函数,咱们从前面分析知道_read_images中有处理非懒加载类代码,咱们打上断点以及打印,如图因为LGPerson没有实现+load函数,因此是懒加载类,所以没有进入咱们的断点。然而LGPerson类在何时加载了呢?咱们研究 非懒加载类的代码发现,实现了realizeClassWithoutSwift函数,所以咱们看懒加载类会不会进入这个函数。spa

4.2.1 realizeClassWithoutSwift

从这里咱们验证了懒加载类的类的加载延迟到第一次消息发送ssr

4.3 非懒加载类分析

咱们让LGPerson实现+load函数,里面什么也不用作,如图此时看_read_images中有关非懒加载类的处理此时咱们看到进入了咱们原先的断点中,且也执行了realizeClassWithoutSwift函数3d

4.4 重点分析realizeClassWithoutSwift函数

重点代码分析 调试

类加载总结

相关文章
相关标签/搜索