要说实例,首先要说是哪个类的实例;要说元类一样要说是哪个类的元类;所以我们这里先从类说起,从定义到底层实现,逐层剥开。
一、类的定义
在objc/runtime.h 头文件,里面有一个结构体定义:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY; ///isa指针,指向所属类
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE; /// 父类
const char * _Nonnull name OBJC2_UNAVAILABLE; ///类名
long version OBJC2_UNAVAILABLE;///类的版本信息,默认为0
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;
/* Use `Class` instead of `struct objc_class *` */
这个结构体所定义的就是类,里面各项数据保存了一个类的所有元数据,我们常说对象的本质是结构体,其实类也是一种对象,是类对象,其本质一样是结构体;其中Class isa 这个指针的类型是什么,点进去你会发现,他是这样定义的:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
isa指针指向的依然是个 object_class 结构体,只不过为了语义化起个名字叫Class;下面是定义一个类的实例:
/// Represents an instance of a class. 表示类实例,类对象
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.指向类实例(对象)的指针。
typedef struct objc_object *id;
因为我们初始化一个类的实例可以直接用id来定义,那么id就是上面这样定义的,所以类的实例初始化完了之后,它的内部就多了一个isa指针,刚我们已经说了这个指针类型指向的是object_class结构体,其实也就是指向了这个实例所属的类。
二、实例(对象)
- 每个对象都有一个类。这是面向对象概念的基本原则,一个对象的类由他的isa指针来决定,isa指针指向对象的类。
- 类包含了一串方法能够被这个类的所有实例对象使用,并且有一个 superclass的指针来查找继承链中的方法。运行时在类和父类的方法列表中查找能够匹配这个消息的 selector,然后唤起这个方法的实现(IMP)。
- OC中对象的最重要的特性是:你能够将类定义的消息发送给一个实例对象。 假设我们已经有一个 YNPerson 类,下面我们创建这个类的实例:
id person = [[YNPerson alloc]init];
实例person里面包含了一个isa指针,这个指针就指向了YNPerson类的元数据,因此我们就能用person对象去调用它的实例方法以及实例变量等功能;
三、元类
为了让我们对一个类调用方法(使用类方法),类的isa指针必须指向一个类结构,并且这个类结构必须包含一个方法列表使我们能够对类使用。这就引导出元类的定义: 元类是一个类对象的类。
- 简而言之:

- 当你向一个对象发送一条消息的时候,运行时会在对象的类的方法列表中查找这条消息是否存在。实例方法存储在类中。
- 当你向一个类发送一条消息的时候,运行时会在类的元类的方法列表中查找这条消息是否存在。类方法存储在元类中。
元类的存在是必需的,因为他存储了一个类的所有类方法。每个类的元类都是独一无二的,因为每个类都有一系列独特的类方法。
四、元类的类
元类,和类一样,也是一个对象。这表示你能够对元类调用方法。自然的,这表示他必须也有一个类指针。
所有元类使用基类的元类(即继承链顶端的类的元类)作为他们的类,而所有类的基类都是 NSObject(大多数类是这样的),所以大多数元类使用 NSObject 的元类作为他的类。
根据规则所有元类使用基类的元类作为他们的类,那么基类的元类就是他自己的类(他们的isa指针指向了自己)。这表明NSObject的元类的指针指向的是他自己(他是一个他自己的实例)。
五、继承类和元类
类使用 super_class 指针指向他们的 superclass,元类也有 super_class 指针来指向 superclass。
这里又有一个奇怪的地方,基类的元类设置的 superclass 是基类自己。 这种继承结构导致的结果是所有结构中的实例、类以及元类都继承自结构中的基类。
所有实例、类和元类都在 NSObject 的层级下,这表明所有 NSObject 的实例方法都能够被使用,同样的,对类以及元类来说,所有 NSObject 的类方法也是有效的。
下面这张图可以帮助理解:

总结
以下是结合动态创建类以及上述理论原理做出的验证示例:Demo地址
void isaPointerFunction(id self,SEL _cmd) {
NSLog(@"This object is %p.", self);
NSLog(@"Class is %@, and super is %@.", [self class], [self superclass]);
// for (Class currentClass = [self class]; currentClass != object_getClass(currentClass); currentClass = object_getClass(currentClass)) {
// NSLog(@"Following the isa pointer %@ and Memory address is %p",currentClass ,currentClass);
// }
Class currentClass = [self class];
while ([[currentClass superclass]superclass] != nil) {
NSLog(@"Following the isa pointer %@ and Memory address is %p,isMetaClass:%d",currentClass ,currentClass,class_isMetaClass(currentClass));
if (class_isMetaClass(object_getClass(currentClass)) && object_getClass(currentClass) == object_getClass([NSObject class])) {
NSLog(@"%@(%p) is Meta Class %@(%p)'s Class", object_getClass(currentClass),object_getClass(currentClass),currentClass,currentClass);
}
currentClass = object_getClass(currentClass);
}
NSLog(@"NSObject's class is %p", [NSObject class]);
NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class]));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
YNPerson *p = [[YNPerson alloc] init];
//输出1
NSLog(@"%d", [p class] == [YNPerson class]);
//输出0
NSLog(@"%d", class_isMetaClass(object_getClass(p)));
//输出1
NSLog(@"%d", class_isMetaClass(object_getClass([YNPerson class])));
//输出0
NSLog(@"%d", object_getClass(p) == object_getClass([YNPerson class]));
Class YNStudent = objc_allocateClassPair([YNPerson class], "YNStudent", 0);
class_addMethod(YNStudent, @selector(getIsaPointer), (IMP)isaPointerFunction, "v@:");
id stu = [[YNStudent alloc]init];
[stu performSelector:@selector(getIsaPointer)];
NSLog(@"Hello, World!");
}
return 0;
}
ClassDemo[1468:1082777] 1
ClassDemo[1468:1082777] 0
ClassDemo[1468:1082777] 1
ClassDemo[1468:1082777] 0
ClassDemo[1468:1082777] This object is 0x103206e80.
ClassDemo[1468:1082777] Class is YNStudent, and super is YNPerson.
ClassDemo[1468:1082777] Following the isa pointer YNStudent and Memory address is 0x103204ef0,isMetaClass:0
ClassDemo[1468:1082777] Following the isa pointer YNStudent and Memory address is 0x103204f20,isMetaClass:1
ClassDemo[1468:1082777] NSObject(0x7fff9674d0f0) is Meta Class YNStudent(0x103204f20)'s Class
ClassDemo[1468:1082777] NSObject's class is 0x7fff9674d140
ClassDemo[1468:1082777] NSObject's meta class is 0x7fff9674d0f0
- 1.元类是一个类对象的类。每一个类有他自己独一无二的元类(因为每个类能够有自己独一无二的方法列表,虽然类名相同,但是地址不同,不是同一概念)。这就意味着元和类并不一样。
- 2.元类能确保类对象拥有所有底层类的实例和类方法,中间加上所有自己的类方法。所有类继承自NSObject,这意味着NSObject所有的实例和协议方法为所有类(和元类)对象都定义了。
- 3.所有元类使用基类的元类(NSObject 元类)来作为他们的类,基类的元类(NSObject 元类)指向自身;
- 4.实例对象的isa指向该类,类的isa指向元类(metaClass)。
- 5.类的superClass指向其父类,如果该类为根类则值为nil。
- 6.元类的superClass指向父元类,若根元类则指向该根类。
由于本人水平有限,文中如有不足之处,望大神指出。
如果你看完后觉得对你有所帮助,勿忘点赞+关注。
附本文的Demo,赠人玫瑰,手有余香。