元类(meta class)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了元类(meta class)相关的知识,希望对你有一定的参考价值。

元类(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)的主要内容,如果未能解决你的问题,请参考以下文章

Java 实现 - 元类

Ruby 元类疯狂

具有自定义元类行为的 Python 元类

1 个类继承 2 个不同的元类(abcmeta 和用户定义的元)

django-元类Meta

为什么python metaclass在此代码中不起作用?