在iOS开发中可能会遇到这样的问题,什么是类对象,它和实例对象有什么区别?
实例对象
首先我们来看看经常使用到的实例对象。
什么是实例?站在面向对象的角度上说,实例是一个抽象类具体的某个对象。
由一个类实例化来的对象叫实例对象。
创建一个Person的对象p
Person *p = [[Person alloc] init];
这个p就是一个实例,也叫做实例对象。
在Runtime中对类的实例做如下定义:
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
从定义里我们可以看到一个实例只有一个isa指针,也就是说实例中只存储了一个isa指针地址。
那么是怎么能通过实例调用到具体类的方法和属性呢?
表示实例对象的结构体objc_object里面有一个Class类型的isa指针,这个指针指向实例所属的类对象,类对象里存储了方法、变量等。通过isa指针就可以找到类对象中的方法了。
类对象(Class)
实例对象所属的类,称为类对象。
引用别人的话来总结:“在Objective-C中,对象是广义的概念,类也是对象,所以严谨的说法应该是类对象和实例对象。既然实例对象所属的类称为类对象,那类对象有所属的类吗?有,称之为元类(Metaclass)”。
Runtime对类对象的定义如下:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
objc_class结构体如下
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
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 *` */
类对象中存储的数据即这个结构体中的数据
isa指针,指向类对象所属的类,即原类。原类中存储着类对象的类方法,当访问某个类的类方法时会通过该isa指针从元类中寻找方法对应的函数指针。
super_class,指向父类的指针,如果该类已经是最顶层的根类(如NSObject或NSProxy), 则 super_class为NULL。
其他的方法名,大小,方法链表,方法缓存链表,协议链表等信息。
元类对象(Metaclass)
“元类就是类对象的类,每个类都有自己的元类,也就是objc_class结构体里面isa指针所指向的类. Objective-C的类方法是使用元类的根本原因,因为其中存储着对应的类对象调用的方法即类方法。“
OC中调用方法,也就是向对象发送消息
-
当向实例对象发送消息时,通过对象的isa指针在对象所属的类中查找方法以及实现。
-
当向类对象发送消息时,通过类对象的isa指针在其元类中寻找方法以及实现。
第一种就是我们常说的调用实例方法的实现过程,第二个就是调用类方法的实现过程。
runtime中可以直接使用以下函数获取元类对象
//获取元类
Class getNSObjectMetaClass = objc_getMetaClass("NSObject");
这个函数在runtime.h中定义的,需要引入头文件然后直接使用。
objc_getMetaClass在runtime中的实现
Class objc_getMetaClass(const char *aClassName)
{
Class cls;
if (!aClassName) return Nil;
cls = objc_getClass (aClassName);
if (!cls)
{
_objc_inform ("class `%s' not linked into application", aClassName);
return Nil;
}
return cls->ISA();
}
看这个函数的实现有助于我们理解isa指针的指向、元类、类之间的关系。
关于isa指针
isa概述
实例对象的isa指针
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
类对象的isa指针
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
...
实例对象的isa–> 实例所属的类对象
类对象的isa–> 类的元类对象
实例对象的isa指针指向类对象
以Person为例,有一个实例p
// 返回Person类对象的本身的地址
Class class0 = [Person class];
// isa指向的Person类对象的地址
Class class1 = [p class];
// isa指向的Person类对象的地址
Class class2 = object_getClass(p);
NSLog(@"class0 -> %p", class0); // -> 0x10a2157d0
NSLog(@"class1 -> %p", class1); // -> 0x10a2157d0
NSLog(@"class2 -> %p", class2); // -> 0x10a2157d0
针对class0有一个问题:
Person是个类对象,它的isa指针不是应该指向元类对象吗,怎么这里打印的地址是自己的地址?
类对象的isa指针指向元类
// 返回Person类对象本身的地址
Class class3 = [class0 class];
// isa指向的Person类对象的元类
Class class4 = object_getClass(class0);
// isa指向Person类对象的元类
Class class5 = object_getClass(class1);
NSLog(@"class3 -> %p", class3); // -> 0x10a2157d0
NSLog(@"class4 -> %p", class4); // -> 0x10a2157f8
NSLog(@"class5 -> %p", class5); // -> 0x10a2157f8
class4、class5打印的就是元类的地址。
元类对象的isa指向根元类
略…
举例
通过打印地址,更好的理解 类对象、元类对象、实例对象、isa指针以及它们之间的关系。
//NSObject的类对象
Class class_NSObject = [NSObject class];
//NSObject类对象的元类对象
Class metaClass_NSObject = object_getClass(class_NSObject);
//NSObject类对象的元类的元类对象,即根元类对象
Class rootMetaClass_NSObject = object_getClass(metaClass_NSObject);
//NSObject的根元类对象的元类对象
Class root_rootMetaClass_NSObject = object_getClass(rootMetaClass_NSObject);
//这里直接用函数获取元类
Class getNSObjectMetaClass = objc_getMetaClass("NSObject");
//NSObject的类对象地址
NSLog(@"class_NSObject -> %p",class_NSObject); // -> 0x1104b8ea8
//NSObject的类对象的元类对象地址
NSLog(@"metaClass_NSObject -> %p",metaClass_NSObject); // -> 0x1104b8e58
//NSObject的类对象的元类对象的元类地址
NSLog(@"rootMetaClass_NSObject -> %p",rootMetaClass_NSObject); // -> 0x1104b8e58
//NSObject的类对象的元类的元类地址
NSLog(@"root_rootMetaClass_NSObject -> %p",root_rootMetaClass_NSObject);// -> 0x1104b8e58
//NSObject的类对象的元类
NSLog(@"getNSObjectMetaClass -> %p",getNSObjectMetaClass);// -> 0x1104b8e58
//Person类对象
Class class_Person = [Person class];
//Person类对象的元类
Class metaClass_Person = object_getClass(class_Person);
//Person类对象的根元类
Class rootMetaClass_Person = object_getClass(metaClass_Person);
//Person类对象的根元类的元类对象
Class root_rootMetaClass_Person = object_getClass(rootMetaClass_Person);
NSLog(@"class_Person -> %p",class_Person); // -> 0x10f4d48b8
NSLog(@"metaClass_Person -> %p",metaClass_Person); // -> 0x10f4d48e0
NSLog(@"rootMetaClass_Person -> %p",rootMetaClass_Person); // -> 0x1104b8e58
NSLog(@"root_rootMetaClass_Person -> %p",root_rootMetaClass_Person); // -> 0x1104b8e58
//对象p所属的类对象,对象p的isa指针指向所属的类
Class pClass = [p class];
//对象p的元类对象,类对象的isa指针指向元类
Class pClass_MetaClass = object_getClass(pClass);
//对象p的元类的元类
Class pClass_MetaClass_MetaClass = object_getClass(pClass_MetaClass);
//对象p的元类的元类的元类
Class pClass_root_rootMetaClass_p = object_getClass(pClass_MetaClass);
//对象p的地址
NSLog(@"p -> %p",p); // -> 0x60800024a710
//对象p的元类地址
NSLog(@"pClass_MetaClass -> %p",pClass_MetaClass); // -> 0x10f4d48e0
//对象p的元类的元类地址
NSLog(@"pClass_MetaClass_MetaClass -> %p",pClass_MetaClass_MetaClass); // -> 0x1104b8e58
//对象p的根元类地址
NSLog(@"pClass_root_rootMetaClass_p -> %p",pClass_root_rootMetaClass_p);// -> 0x1104b8e58
//对象p的根元类的元类地址
NSLog(@"pClass_MetaClass_MetaClass -> %p",pClass_MetaClass_MetaClass); // -> 0x1104b8e58
-
NSObject的元类的isa指针指向自身,即NSObject的元类就是其根元类
-
实例对象的isa指针指向其所属的类,类的isa指向其元类
-
元类的isa指向根元类,根元类的isa指针指向自身
-
所有继承于NSObject的类的元类的根元类都是同一个类,即NSObject的元类/根元类