元类(meta class)

作者: 起个名字想破头 | 来源:发表于2017-04-26 13:59 被阅读1071次

    元类(meta class),这个名字想必很多人都听过,网上也有很多关于元类的介绍,今天我就按照自己这两天的理解来简单探讨一下这个玩意,有误之处还望指出。

    首先,下载objc源码,源码地址:https://opensource.apple.com/tarballs/objc4/
    打开链接后会发现有很多版本,我直接下载的最新版(709版本)

    认识NSObject

    1.打开objc工程的NSObject.h,找到NSObject类的定义
    @interface NSObject <NSObject> {
        Class isa  OBJC_ISA_AVAILABILITY;  //发现NSObject包含一个Class对象
    }
    
    2.继续查看Class类型
    typedef struct objc_class *Class;
    
    3.继续查看objc_class类型
    struct objc_class : objc_object {  //objc_class继承自objc_object
        Class superclass;
        const char *name;
        uint32_t version;
        uint32_t info;
        ...
    }
    
    4.查看objc_object类型
    struct objc_object {
    private:
        isa_t isa;  //这个东西好像很牛逼,继续看一下
        ...
    }
    
    5.查看isa_t类型
    union isa_t 
    {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;    //诶,这个东西又出现了
        uintptr_t bits;
    }
    

    简单用图片表示一下:


    图-1

    那么简单总结一下就是,NSObject包含一个Class,Class中包含一个superclass和cls

    meta class 获取

    1.打开objc工程的runtime.h,找到meta class的获取接口
    Class objc_getMetaClass(const char *name) {
        //下面代码进行了简化
        //1.通过类名找到类对象(注意不是类实例对象)
        Class cls = objc_getClass (aClassName);
        //2.返回上面图-1中最里面的cls
        return cls->isa.cls;
    }
    
    2.meta class的获取其实就是获取类对象包含的cls,而我们在实际开发中是不能调用到cls的,这时,可以通过object_getClass来实现。
    查看object_getClass的实现
    Class object_getClass(id obj) {
        //下面代码经过简化
        return obj->isa.cls;  //也是返回cls
    }
    

    通过测试代码来分析源码

    //引入rumtime头文件
    #import <objc/objc-runtime.h>
    //定义一个简单的类
    @interface Father : NSObject
    @property (nonatomic, strong) NSString *name;
    @end
    //各种实例打印
        Father* f = [[Father alloc] init];
        
        NSLog(@"f address:%p", f);
        NSLog(@"[f class] address:%p", [f class]);
        NSLog(@"[Father class] address:%p", [Father class]);
        NSLog(@"objc_getMetaClass address:%p", objc_getMetaClass("Father"));
        NSLog(@"objc_getClass address:%p", object_getClass([Father class]));
    

    打印结果:

    打印结果:
    2017-04-26 11:46:37.104 runtimeDemo[190:22480332] f address:0x600000011e70
    2017-04-26 11:46:37.104 runtimeDemo[190:22480332] [f class] address:0x1063d5ff0
    2017-04-26 11:46:37.105 runtimeDemo[190:22480332] [Father class] address:0x1063d5ff0
    2017-04-26 11:46:37.105 runtimeDemo[190:22480332] objc_getMetaClass address:0x1063d5fc8
    2017-04-26 11:46:37.105 runtimeDemo[190:22480332] objc_getClass address:0x1063d5fc8
    

    通过结果进行分析:

    • 类实例对象本身不是元类
    • 类实例对象通过class方法获取到的对象为类对象,[f class] == [Father class]
    • 通过类对象调用的object_getClass得到的是meta class

    meta class 继承

    meta class继承图

    这是一张很牛逼的图,我们用尽量简单的代码来测试一下:

        Son *s = [[Son alloc] init];    //Instance of Subclass
        Class cls = [s class];     //Subclass class
        Class meta = object_getClass(cls);  //Subclass meta
        
        Class superclass = [cls superclass];
        Class supermeta = [meta superclass];
        Class supermeta2 = object_getClass(superclass);
        
        Class rootclass = [superclass superclass];
        Class rootmeta = [supermeta superclass];
        Class rootmeta2 = object_getClass(rootclass);
        
        Class nilclass = [rootclass superclass];
        Class superrootmeta = [rootmeta superclass];
        
        NSLog(@"s address:%p", s);
        NSLog(@"cls address:%p", cls);
        NSLog(@"meta address:%p", meta);
        NSLog(@"superclass address:%p", superclass);
        NSLog(@"supermeta address:%p", supermeta);
        NSLog(@"supermeta2 address:%p", supermeta2);
        NSLog(@"rootclass address:%p", rootclass);
        NSLog(@"rootmeta address:%p", rootmeta);
        NSLog(@"rootmeta2 address:%p", rootmeta2);
        NSLog(@"nilclass address:%p", nilclass);
        NSLog(@"superrootmeta address:%p", superrootmeta);
    

    打印结果

    2017-04-26 12:32:10.412 runtimeDemo[1194:22584480] s address:0x608000015700
    2017-04-26 12:32:10.412 runtimeDemo[1194:22584480] cls address:0x10540f060
    2017-04-26 12:32:10.412 runtimeDemo[1194:22584480] meta address:0x10540f038
    2017-04-26 12:32:10.412 runtimeDemo[1194:22584480] superclass address:0x10540f0b0
    2017-04-26 12:32:10.413 runtimeDemo[1194:22584480] supermeta address:0x10540f088
    2017-04-26 12:32:10.413 runtimeDemo[1194:22584480] supermeta2 address:0x10540f088
    2017-04-26 12:32:10.413 runtimeDemo[1194:22584480] rootclass address:0x105da8e88
    2017-04-26 12:32:10.413 runtimeDemo[1194:22584480] rootmeta address:0x105da8e38
    2017-04-26 12:32:10.413 runtimeDemo[1194:22584480] rootmeta2 address:0x105da8e38
    2017-04-26 12:32:10.413 runtimeDemo[1194:22584480] nilclass address:0x0
    2017-04-26 12:32:10.413 runtimeDemo[1194:22584480] superrootmeta address:0x105da8e88
    

    通过结果可以看出,结果与图示相符。

    FAQ:
    1.class方法和object_getClass有区别么?
    细心的朋友可能发现了,上面有的时候用class方法,有的时候用object_getClass方法。让我们看一下源码

    + (Class)class {
        return self;
    }
    - (Class)class {
        return object_getClass(self);
    }
    
    • 类方法class,返回的是self,所以当查找meta class时,需要对类对象调用object_getClass方法
    • 实例方法class,内部实现就是调用的object_getClass方法,
      即实例对象调用class,或对实例对象使用object_getClass()时,返回的确实是实例对象的cls,但实例对象内部的cls保存的是类对象,而不是meta class

    ps: 附上一些有关meta class的文章
    http://www.jianshu.com/p/45fe90253519
    http://blog.csdn.net/beclosedtomyheart/article/details/50164353
    http://blog.csdn.net/windyitian/article/details/19810875

    相关文章

      网友评论

        本文标题:元类(meta class)

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