iOS底层探索 之 类的加载

670 阅读4分钟
_objc_init

environ_init 环境变量初始化

    for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
        const option_t *opt = &Settings[i];
        _objc_inform("%s: %s", opt->env, opt->help);
        _objc_inform("%s is set", opt->env);
    }

打印如下

OBJC_DISABLE_NONPOINTER_ISA 是什么呢?

设置环境变量

static_init jump 全部静态C++函数调用

getLibobjcInitializers jump

runtime_init jump

UnattachedCategories jump 独立类别 表 static UnattachedCategories unattachedCategories;

allocatedClasses jump 分配的类 表

init

exception_init 异常抛出

installUncaughtSignalExceptionHandler

数组越界 工程没有崩溃 拦截到了异常

//重点代码  指针传递 能够同步发生变化 类似于指针拷贝
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
map_images jump

map_images_nolock jump

_read_images 重点

initializeTaggedPointerObfuscator 初始化标记指针模糊器 (混淆)

//重点代码
 // namedClasses      
// Preoptimized classes don't go in this table.       
// 4/3 is NXMapTable's load factor
//总容积 : 8 * 4 /  3
//NXCreateMapTable表 add: x =  8 * 4 /  3  * 3 / 4

int namedClassesSize =  (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3; 

gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype,namedClassesSize);  //NXCreateMapTable 创建表

 ts.log("IMAGE TIMES: first time tasks");

gdb_objc_realized_classes 表
//另外的表
    objc::unattachedCategories.init(32);
    objc::allocatedClasses.init();

gdb_objc_realized_classes、allocatedClasses这俩表有什么区别呢?

gdb_objc_realized_classes 总表 不管实现与否,可以是没被开辟过的 allocatedClasses 总表 已经被开辟过的

_read_images 下断点

输出

sel_registerNameNoLock jump

__sel_registerName jump

search_builtins jump _dyld_get_objc_selector

_read_images-resolvedFutureClasses 已解析的未来类 在加载的过程中,有的类在读取之后就会被删除掉,但是删除没有删干净,就会出现一些混乱(野指针),也就是残留的类。

po 打印

_getObjc2ClassRefs jump
readClass

          //重点代码
            class_rw_t *rw = newCls->data();
            const class_ro_t *old_ro = rw->ro();
            memcpy(newCls, cls, sizeof(objc_class));

            // Manually set address-discriminated ptrauthed fields
            // so that newCls gets the correct signatures.
            newCls->setSuperclass(cls->getSuperclass());
            newCls->initIsa(cls->getIsa());

            rw->set_ro((class_ro_t *)newCls->data());
            newCls->setData(rw);
            freeIfMutable((char *)old_ro->getName());
            free((void *)old_ro);

            addRemappedClass(cls, newCls);

输出

加入判断条件 控制台只输出 readClass - HL - HLPerson

addNamedClass jump

addClassTableEntry jump

待续..

addNamedClass 里调用 NXMapInsert jump

image.png

_read_images
//重点代码
      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;
            }
        }

接下来进行试探

1
    // Realize non-lazy classes (for +load methods and static instances)
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            
            //HL
            const char *mangledName = cls->nonlazyMangledName();

            const char *HLPersonName = "HLPerson";
            
            if (strcmp(mangledName, HLPersonName) == 0) {
                
                printf("Realize non-lazy-%s - HL - %s\n",__func__,mangledName);

            }
            //HL

2
 if (resolvedFutureClasses) {
        for (i = 0; i < resolvedFutureClassCount; i++) {
            Class cls = resolvedFutureClasses[i];
            
            //HL
            const char *mangledName = cls->nonlazyMangledName();

            const char *HLPersonName = "HLPerson";
            
            if (strcmp(mangledName, HLPersonName) == 0) {
                
                printf("resolvedFutureClasses-%s - HL - %s\n",__func__,mangledName);

            }
            //HL

上面两个判断都没进

realizeClassWithoutSwift 核心重点

image.png

image.png

//rw赋值 (ro 赋值给 -> rw)
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);

setInstancesRequireRawIsa //集合实例需要原始Isa

  • DisableNonpointerIsa //环境变量

image.png

继承链 isa走位

supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil); metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

  • 添加判断

image.png

image.png

  • 没有打印出*$2的地址

image.png

image.png

  • 打印

image.png

prepareMethodLists jump

image.png

fixupMethodList jump

image.png

meth.name jump

image.png

image.png

image.png

image.png

打印ro 未打印出*$2的值

image.png

rwe 什么时候赋值呢?

image.png

realizeAndInitializeIfNeeded_locked jump

image.png

realizeClassMaybeSwiftAndLeaveLocked jump

image.png

realizeClassMaybeSwiftMaybeRelock jump

image.png

验证了只要是消息发送的时候类方法和实例方法 所属类对象都可以进行加载

懒加载类情况 数据加载推迟到第一次消息的时候 lookUpImpOrForward realizeClassMaybeSwiftMaybeRelock realizeClassWithoutSwift methodizeClass

非懒加载类情况 map_images 的时候加载所有类数据 redClass _getObjc2NonlazyClassList realizeClassWithoutSwift methodizeClass

分类是怎么加载的?怎么调用的?

attachToClass

image.png

methodizeClass 
    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }

 auto rwe = rw->ext();

txt jump
    class_rw_ext_t *ext() const {
        return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
    }

    class_rw_ext_t *extAllocIfNeeded() {
        auto v = get_ro_or_rwe();
        if (fastpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
        } else {
            return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
        }
    }

extAllocIfNeeded jump

  • 1、attachCategories 添加分类时 调用
  • 2、class_setVersion设置类版本时
  • 3、addMethods_finish 添加方法完成时
  • 4、class_addProtocol 类添加协议
  • 5、_class_addProperty
  • 6、objc_duplicateClass

分类什么时候加载的?

  • attachCategories调用 重点
  • 1、attachToClass
  • 2、load_categories_nolock

提出一个疑问为什么要有rwe、rw、ro?三者之间又是怎么样的联系? ro clear m沙河路径加载过来的 readonly ro 复制一份寸在rw rw 脏内存 运行时功能

  • 1、添加分类、添加的方法
  • 2、api addMethods ro 是readonly 不能修改,所以就得用rw 但是并不是每个类都会用到分类,就会产生脏内存,这时候就用到了rwe。 类里面 有方法 协议 属性 名字 父类
  • read_image clas->data

image.png

auto ro = (const class_ro_t *)cls->data();

打印出 realizeClassWithoutSwift - realizeClassWithoutSwift - HL - HLPerson

image.png

class_getClassMethod jump

image.png

image.png

根据格式还原

image.png

auto ro = (const class_ro_t *)cls->data();

自动去找到当前的类型,进行挨个赋值,除非当前的格式地址不匹配无法解析,所以ro从Mach-O里面读到地址指针,可以对class_ro_t的所有相应数据进行赋值。

rwe赋值

extAllocIfNeeded

image.png

attachCategories

image.png

methodizeClass方法里3次调用attachToClass

image.png

methodizeClass - previously = nil

image.png

realizeClassWithoutSwift - previously

image.png

attachLists

image.png

观察分类是否已经到主类里面来了

image.png

image.png

image.png

打印完21个方法,还是没有看到分类的方法

image.png

通过观察发现(method_list_t *) $0 = 0x00007fff80ad89c8 与 [63] = 0x00007fff80ad89c8 是一致的

p mlists + ATTACH_BUFSIZ - mcount

image.png

p addedLists

image.png

两个值是一样的

image.png

image.png

总结

  • 1: 两个都有 load方法 : _read_images 非赖加载类 - realizeClassWithoutSwift - load_categories_nolock - attachCategories
  • 2: 分类 load + 主类没有 ( data()里面获取 ) _read_images - realizeClassWithoutSwift - methodizeClass - attachToClass - 没有走 attachCategories
  • 3: 分类 没有 + 主类 load ( data() ) _read_images - realizeClassWithoutSwift - methodizeClass - attachToClass - 没有走 attachCategories
  • 4: 两个都没有 main 什么都没有 -> 推迟到 第一次消息发送的时候 初始化 -> data()
  • 5: 两个都有 load方法 - 很多的分类 (分类不都有load)