元类(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)的更多相关文章

  1. django-model的元类Meta

    Meta类存在model类里面 模型元选项 文档有更多Meta类的配置属性: English:https://docs.djangoproject.com/en/1.11/ref/models/opt ...

  2. iOS中类、元类、isa详解

    类相信大家都知道是什么,如果看过runtime的源码或者看过相关的文章对isa肯定也不陌生,不过元类(meta class)大家可能就比较陌生了.不过大家也不要担心,我会细细道来,让大家明白它到底是个 ...

  3. Delphi 类引用 Class Reference 元类 MetaClass 用法

    delphi中类引用的使用实例 类引用类引用(Class Reference)是一种数据类型,有时又称为元类(MetaClass),是类的类型的引用.类引用的定义形式如下: class of type ...

  4. Python 元类详解

    一.Type介绍 在Python中一切皆对象,类它也是对象,而元类其实就是用来创建类的对象(由于一切皆对象,所以元类其实也是一个对象). 先来看这几个例子: 例1: In [1]: type(12) ...

  5. Python中的元类和__metaclass__

    1.什么是元类 元类让你来定义某些类是如何被创建的,从根本上说,赋予你如何创建类的控制权.可以把元类想成是一个类中类,或是一个类,它的实例是其它的类.当某个类调用type()函数时,你就会看到它到底是 ...

  6. Python元类实践--自己定义一个和collections中一样的namedtuple

    大家可能很熟悉在collections模块中有一个很好用的扩展数据类型-namedtuple. 如果你还不知道这个类型,那么请翻看标准手册. 我利用元类轻松定义一个namedtuple. 先把代码贴上 ...

  7. Python属性、方法和类管理系列之----元类

    元类的介绍 请看位于下面网址的一篇文章,写的相当好. http://blog.jobbole.com/21351/ 实例补充 class Meta(type): def __new__(meta, c ...

  8. Python的元类

    1.用元类验证子类 每当我们定义新类的时候,元类就会运行雅正代码,以确保这个新类符合规定的规范. Python系统把子类的class语句处理完毕,就会调用元类的 __new__ 方法.元类可以通过 _ ...

  9. Python中的元类(metaclass)

    推荐+收藏:深刻理解Python中的元类(metaclass) 做一些笔记学习学习: 在大多数编程语言中,类就是用来描述如何生成一个对象的代码段,在Python中类也是一个对象,这个(类)对象自身拥有 ...

随机推荐

  1. python 语句:条件、循环、break、continue...

    1. 条件语句 执行条件:判断条件"成立时(非零),则执行后面的语句,而执行内容可以多行,以缩进来区分表示同一范围. [Python程序语言指定任何非0和非空(null)值为true,0 或 ...

  2. Python可视化学习(2):Matplotlib快速绘图基础

    Matplotlib将大部分的绘图对象都封装成为对象,故理论上所有的图表元素(如Line2D, Text,Label等)都是对象,都可以将其在图表中提取出来并配置实例的属性.同时,Matplotlib ...

  3. UIScrollView的布局

    一.UIScrollView的子控件布局不能跟其它的控件一样进行布局,因为UIScrollView会根据子控件计算出ContentSize的大小,那么我们应该如何进行布局呀. 遵循以下两点就行 1.参 ...

  4. node压力测试

    压力测试 ab测试(ApacheBench); 介绍: 这是apache提供的压测工具; 使用: 启动node服务; 我用的XAMPP,进入bin文件夹,打开命令行,执行下面命令: // -n: 总请 ...

  5. package(1):tm

    tm包是R语言中为文本挖掘提供综合性处理的package,进行操作前载入tm包,vignette命令可以让你得到相关的文档说明.使用默认安装的R平台是不带tm  package的,在安装的过程中,它会 ...

  6. .NET遇上Docker - Harbor的安装与基本使用

    Harbor是一个开源企业级Docker注册中心,可以用于搭建私有的Docker Image仓库.可以实现权限控制等. 安装Harbor 首先,需要安装Docker和Docker Compose,参考 ...

  7. 利用_winreg模块在注册表中分析无线访问热点

    _winreg.OpenKey(key, sub_key, res, sam)     key是一个已经打开的键,或者是HKEY_CLASSES_ROOT.HKEY_CURRENT_USER.HKEY ...

  8. 学习css之文本属性

    css3之文本属性: 1.缩进和水平对齐:text-indent, 通过使用 text-indent 属性,所有元素的第一行都可以缩进一个给定的长度,甚至该长度可以是负值. 这个属性最常见的用途是将段 ...

  9. Java 比较(==, equals, compareTo, compare)

    在Java中,有 ==, equals(), compareTo(), compare() 等方法可以比较两个值或对象,比较容易混淆.画了个简单的思维导图总结一下 Java Compares 我经常记 ...

  10. MySQL常见建表选项以约束

    一.CREATE TABLE 选项 1.在定义列的时候,指定列选项 1)DEFAULT <literal>:定义列的默认值 当插入一个新行到表中并且没有给该列明确赋值时,如果定义了列的默认 ...