美文网首页
笔记-如何优雅姿势探究类结构(类的底层原理解析)

笔记-如何优雅姿势探究类结构(类的底层原理解析)

作者: 佐_笾 | 来源:发表于2019-04-14 21:00 被阅读0次

    类的底层原理

    实例对象、类对象、元类之间的关系

    直接上代码,看结果之后解释一下


    image

    思考一下这几个问题:类对象class1class2class3打印的地址分别是什么情况?
    为什么class4是元类,class5是根元类?

    打印结果


    image

    可以看出:
    类对象class1class2class3的地址是同一个,因为一个对象的类对象只有一个。
    object_getClass获取对象的类,类对象存储的位置是哪里?在文章笔记-runtime源码解析之让你彻底了解底层源码里讲述过,它是存在元类中,所以class4为元类,同样class5为根元类。如果还有疑问的话,可以接着往后看,或者评论里留言给笔者。
    下面进行一些lldb调试,直接上图

    image
    • person->isa输出了person对象的isa的指向,是ZBPerson这个类,地址是0x6000034232c0,调试的输出的结果和打印输出的结果一致
    • 用命令x 0x6000034232c0输出的是ZBPerson这个类内存的情况,图上说明了,前八字节指的是isa,为什么呢?看下面几段源码
    image
    Classobjc_class类型的
    image image
    上面一段源码里可以看出,类的内部结构,前8字节为隐藏属性isa,接着后8字节是superclass
    接着是16字节的cache等等,具体的后面分析。
    • 上面lldb调试过程中也说类,isa为优化过的,每次打印输出的时候,都&上了一个值0x00007ffffffffff8,这又是从哪里得出来的结论呢?请看下面源码:
    image
    image
    嗯,讲述到这里,上面的lldb调试的过程,相信你是可以明白的,其实最终还是回到文章笔记-runtime源码解析之让你彻底了解底层源码里的一幅图
    image

    类结构

    直接上源码

    image
    上面源码,我只截图了部分代码,下面其实还有很长,这里不做说明。
    isa、superclass、cache在上面简单描述了,这里不再重复,着重看下面,直接看下面代码
    image
    这里有我们熟悉的methods、properties、protocols,往下走 image
    image

    这里的list_array_tt是一个二级指针型的,存放着我们常用的属性列表、方法列表、协议列表。
    上面的截图,我也只截图了一部分,里面有个标红的protected,其实还有着privatepublic
    这又与我们日常的开发的公有属性、私有属性等等相呼应。

    除了methods、properties、protocols,还有一个class_ro_t需要看一下,上源码

    image

    这里有到面试题,看下面代码:

        Class ZBClass = objc_allocateClassPair([NSObject class], "ZBClass", 0);
        // 添加方法--属性
        NSString *name = @"name";
        
        objc_registerClassPair(ZBClass);
        class_addIvar(ZBClass, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id));
        
        id zbClass = [ZBClass alloc];
        [zbClass setValue:@"Zuobian" forKey:@"name"];
        NSLog(@"name == %@", [zbClass valueForKey:@"name"]);
    

    上面这段代码,能否正常运行?

    image
    报错指出没有这个key,但是上面代码中确实已经添加,那么只是说明添加失败了,为什么呢?

    如果把这两句话颠倒一下,打印查看结果

        class_addIvar(ZBClass, name.UTF8String, sizeof(id), log2(sizeof(id)), @encode(id));
        objc_registerClassPair(ZBClass);
    
    image
    说明在注册这个函数之前添加Ivar是成功的,回到class_ro_t源码可以看出
    image
    ivar_list_tconst。一旦这个类创建完毕,就不能进行修改.

    MachOView查看类结构

    先编译运行项目,然后找到可执行文件

    image
    image
    image
    把这个可执行文件拖到MachOView里,下图显示
    image

    然后打印出当前类的地址,通过image list找到首地址,通过计算器算出偏移量

    image
    得到结果0x3FE0,然后到MachOView里查找
    image

    通过lldb调试查看类结构

    编译运行下面代码

    image

    得到里类对象以及元类对象的地址。

    image
    通过源码可以知道,我们想要得到class_rw_t *data(),就需要知道class_data_bits_t bits,而要知道class_data_bits_t bits,我们就需要通过类对象的地址进行指针偏移来获得。8字节+8字节+16字节--->移2位便能获取到class_data_bits_t bits
    image
    找到class_rw_t *data()后,打印出来
    image
    对比下面的源码看一下
    image
    对比之后,是不是我们想要的东西都在里面class_ro_t、methods、properties、protocols

    再进入class_ro_t细看一下

    image
    输出的结果很明确里,当打印baseMethodList时,还同时给出里方法名、方法签名、所在的类以及多少行;有兴趣的读者还可以通过这种方式打印出类里的其他内容。

    这一切看上去似乎很完美,给大家看一下ZBPerson.m文件里的内容

    image
    那么问题就来了,上面的lldb打印只打印出了instanceMethod方法,那其他两个方法都去哪里了呢?

    nice~类方法存储在元类中,上面调试的都是类对象的结构,下面的就是类方法的调试


    image

    相关文章

      网友评论

          本文标题:笔记-如何优雅姿势探究类结构(类的底层原理解析)

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