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

2,054 阅读3分钟

1 前言

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

2 先看_read_images函数

由于我们探究的是类的加载,且在iOS底层原理之—dyld与objc的关联中我们已经主要分析了下_read_images函数的作用,因此我们主要看_read_images函数中有关的部分代码,上代码

// 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函数。

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类型的代码

3.2 代码跟踪

通过代码调试,最终执行了addNamedClsss函数在执行addNamedClsss函数之前打印cls依然是一串地址。当执行完addNamedClsss函数时,打印cls发现指向了LGPerson因此我们得出addNamedClsss对cls进行了指向操作。

3.3 addNamedClsss函数

4 懒加载类与非懒加载类

4.1 定义

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

4.2 懒加载类分析

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

4.2.1 realizeClassWithoutSwift

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

4.3 非懒加载类分析

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

4.4 重点分析realizeClassWithoutSwift函数

重点代码分析

类加载总结