美文网首页
续写Objective-C的本质

续写Objective-C的本质

作者: IBigLiang | 来源:发表于2018-12-25 11:28 被阅读0次

    在上一篇文章iOS-Objective-C的本质中,笔者主要分析了三个面试题中的第一个问题。然后针对它做了一个稍微深入的分析,这也算是对OC对面的内存分配的一个小总结
    接下来我们再来看看剩余的两个问题,首先我再次把问题抛出来:
    2、对象的isa指针指向哪里
    3、OC的类信息存放在哪里
    首先,笔者先给出这两个问题的答案,这样要是之前已经了解过的同学可以直接总结回顾,要是了解的不是很全面的同学,我们在一起具体分析下:
    问题一:对象的isa指针指向哪里
    答:instance(实例)对象的isa指向class(类)对象;class对象的isa指向meta-class(原类)对象;meta-class类的isa指向基类的meta-class对象。
    问题二:OC的类信息存放在哪里
    答:对象方法、属性、成员变量、协议信息,存放在class对象中;类方法,存放在meta-class对象中;成员变量的具体值,存放在instance对象中
    其实针对于上面这些回答,有一张很好的图可以说明,如下:

    image.png
    接下来,我们一步一步来分析这几个结论是怎么出来的,我们先来看看如下代码:
    @interface BLPerson : NSObject
    @end
    @implementation BLPerson
    @end
    
    int main(int argc, const char * argv[]) {
        BLPerson *person = [[BLPerson alloc] init];
        Class personClass = object_getClass(person);
        NSLog(@"person --- %p", person);
        NSLog(@"personClass --- %p", personClass);
        return 0;//此处打断点,观察person和personClass的情况
    }
    

    我们看看控制台的打印:


    image.png

    大家应该可以看到两个红色框框中是同一个地址;先说明下,p/x 这个命令行就是打印16进制书,把isa先强制转换成long,然后在用16进制打印,这样我们看到的就是熟悉的地址形式。

    当然心细的同学发现了笔者在打印地址之后,做了一个&操作,二在&0x00007ffffffffff8之后,得到的结果就是我们类对象的地址,这是为什么呢?
    其实在很早的时候,instance对象的isa确实直接指向class对象地址,不过后来苹果公司加了一层&ISA_MASK操作,就是在instance对象isa基础上& ISA_MASK之后,得到的结果就是class对象地址,这个ISA_MASK宏可以在源码objc-4中找到,在不同系统下,它对应的值是不一样的,源码地址,在上一篇文章中笔者已经发过了。

    接下来,我们在根据这个方法,来看看class对象中的isa指针是否指向meta-class的地址,代码如下:

    BLPerson *person = [[BLPerson alloc] init];//实例对象
        Class personClass = object_getClass(person);//类对象
        Class personMetaClass = object_getClass(personClass);//元类对象
        
        NSLog(@"person --- %p", person);
        NSLog(@"personClass --- %p", personClass);
        NSLog(@"personMetaClass --- %p", personMetaClass);
    
    image.png

    这个时候,大家会发现,打印不出来isa的地址;这里我们做一个对象和结构体的转换。在上一篇文章中,我们就知道,其实对象的本质就是一个结构体,根据这个原理,我们代码做一下改变:

    struct BLPerson_OBJC {
        Class isa;
    };
    int main(int argc, const char * argv[]) {
        BLPerson *person = [[BLPerson alloc] init];//实例对象
        Class personClass = object_getClass(person);//类对象
        struct BLPerson_OBJC *personClass_Struct = (__bridge struct BLPerson_OBJC *)(personClass);
        Class personMetaClass = object_getClass(personClass);//元类对象
        
        NSLog(@"person --- %p", person);
        NSLog(@"personClass --- %p", personClass);
        NSLog(@"personMetaClass --- %p", personMetaClass);
        return 0;
    }
    

    这个时候我们在重新打印类对象的isa指向:


    image.png

    可以很清晰的看到,对象的isa&ISA_MASK之后,就是原类对象的地址

    所以元类对象的isa指针& ISA_MASK之后,就是基类的meta-class对象地址,这个结论大家可以照上面的方法去分析,比方说,我们上面的BLPerson是继承自NSObject这个基类的,所以,BLPerson的元类对象isa& ISA_MASK之后就是NSObject元类对象的地址,这里具体我们就不做展开了

    接下来,我们来看看最后一个问题:OC的类信息存放在哪里
    答:对象方法、属性、成员变量、协议信息,存放在class对象中;类方法,存放在meta-class对象中;成员变量的具体值,存放在instance对象中

    当然看过runtime类的代码的同学,应该有所了解

    struct objc_class {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    
    #if !__OBJC2__
        Class _Nullable super_class                              OBJC2_UNAVAILABLE;
        const char * _Nonnull name                               OBJC2_UNAVAILABLE;
        long version                                             OBJC2_UNAVAILABLE;
        long info                                                OBJC2_UNAVAILABLE;
        long instance_size                                       OBJC2_UNAVAILABLE;
        struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
        struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
        struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
        struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
    #endif
    
    } OBJC2_UNAVAILABLE;
    

    在objc_class结构体中其实包含了,一个类的很多信息,我们也可以通过runtime运行时机制,去获取它的诸多信息,这里笔者通过自己所看的资料来总结下。
    其实上面的代码中,大家可以看到#if !OBJC2这个if语句,说明,其实在最新版本的底层代码中,objc_class是不直接暴露if里面所包含的name、version、info等信息的,那这个objc_class结构体真正的样子是怎么样的呢?我们还是可以通过源码去了解,其实它的源码是这样的:

    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
        class_rw_t *data() { 
            return bits.data();
        }
        void setData(class_rw_t *newData) {
            bits.setData(newData);
        }
    
        void setInfo(uint32_t set) {
            assert(isFuture()  ||  isRealized());
            data()->setFlags(set);
        }
    ......
    }
    

    我只列举了一小部分,大家会发现,objc_class是继承自objc_object,而objc_object是这样子的:

    struct objc_object {
    private:
        isa_t isa;
    
    public:
    
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
    ......
    }
    
    

    在objc_class这个结构体中,我们可以看到class_rw_t这个结构体,它内部结构是这样子的:

    struct class_rw_t {
        // Be warned that Symbolication knows the layout of this structure.
        uint32_t flags;
        uint32_t version;
    
        const class_ro_t *ro;
    
        method_array_t methods;
        property_array_t properties;
        protocol_array_t protocols;
    ......
    }
    

    到此,大家就了解到一个类的isa,属性信息,方法信息,协议信息,在底层代码中真正的分布情况,这个源代码还是在objc-4中。
    接下来我们还是用代码来看看类对象和元类对象中的信息,这个代码我就直接用资料代码了,到时候我会分享链接地址,主要还是封装了底层代码,有便于我们分析问题:

            // main 文件中的主要代码,对应的类申明都在main文件中,大家可以下来源代码查看
            MJStudent *stu = [[MJStudent alloc] init];
            stu->_weight = 10;
            
            mj_objc_class *studentClass = (__bridge mj_objc_class *)([MJStudent class]);
            mj_objc_class *personClass = (__bridge mj_objc_class *)([MJPerson class]);
            
            class_rw_t *studentClassData = studentClass->data();
            class_rw_t *personClassData = personClass->data();
            
            class_rw_t *studentMetaClassData = studentClass->metaClass()->data();
            class_rw_t *personMetaClassData = personClass->metaClass()->data();
    

    控制台打印如下:


    image.png

    这张图显示的是类对象中所包含的对象方法、属性信息、协议信息等


    image.png
    这张图显示的是元类对象中所包含的类方法等信息。
    别的信息,大家可以根据自己想看的在代码中了解,当然“成员变量的具体值,存放在instance对象中”这个结论笔者就不分析了,在上一篇文章中,大家就了解到了。

    以上就是笔者对最近所看的资料的总结,也希望对大家有所帮助,要是笔者有问题的地方,同学们尽管提,相互交流

    源代码地址:https://github.com/IBIgLiang/OBJC-INFO

    相关文章

      网友评论

          本文标题:续写Objective-C的本质

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