Objective-C是一个基于类的对象系统。每个对象都是某个类的实例;对象的 isa 指针指向其类。类不但描述其对象的内存分配大小以及变量的类型和布局。而且还描述了对象响应的方法(selectors)以及实现的实例方法。
Ps: 选择器可以理解为方法
类的方法列表是实例方法的集合,这些实例方法是对象响应的选择器。当你向实例对象发送消息时,objc_msgSend() 会查看该对象的类(及其父类,如果有)的方法列表,以确定要调用的方法。
每个 Objective- C类也是一个对象。它也有一个 isa 指针和其他数据(类的数据结构),并且可以响应方法。当你调用像[NSObject alloc]
的类方法时,实际上是向该类对象发送一条消息。
因为类是对象,所以它也一定是其他类的实例,而这个类就是元类。元类是类对象的类(描述),就像类是普通实例的类(描述)一样。此外,元类的方法列表里面都是类方法,类对象都可以调用这些方法。当你向类(元类的实例)发送消息时,objc_msgSend()将查找元类(及其父类,如果有)的方法列表,以确定要调用的方法。类方法由元类代表类对象描述,就像实例方法由类代表实例对象描述一样。
元类呢?它一直都是元类吗?不。元类是根类的元类的一个实例。根元类本身就是根元类的实例。 isa 链最终以一个循环结束:实例 --> 类 --> 元类 --> 根元类 --> 指向自己
。元类 isa 指针一般很少有问题,因为在现实世界中,没有人会把消息发送给元类对象。
更重要的是元类的父类。元类的父类链与类的父类链平行,因此类方法与实例方法并行继承。根元类的父类是它自己(根元类),因此每个类对象都会响应根类的实例方法(例如 NSObject 类既可以调用其类方法,也可以调用其实例方法)。最后,类对象是根类的实例,就像其他对象一样。
困惑?下图可能会有所帮助。请记住,当给任意对象发送消息时,方法查找从该对象的 isa 指针开始,然后沿着父类链一直往下查找。实例方法由类定义,类方法由元类加根类定义 (可以理解为:类方法 = 元类的类方法 + NSObject的实例方法)。
实例、类、元类以及他们父类之间的关系图
在真正的计算机科学语言理论中,类和元类的层次结构可以更加自由,并且具有更深的元类链和可以从任何元类实例化出多个类。Objective-C
使用元类来实现类方法等实际目标,但是往往会隐藏元类。例如,[NSObject class]
跟[NSObject self]
是一样的,即使从形式上讲,它也应返回NSObject->isa
指向的元类 。Objective-C
语言其实是一套适用的折中方案;在这里,meta 让它变得更好,同时它也限制了类的模式。