简介
OC是一门面向对象语言,面向对象语言通常使用类(Class)描述一类事物的基本特性,包括属性和具备的行为等,OC每一个实例对象都包含一个isa指针,用来表明这个对象本质上是个什么。
基本结构
通常我们使用id代表任意对象,说明id其实就一个泛类,或者说基类,打开objc.h头文件,可以看到id的定义
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
typedef struct objc_object *id;
实际上每一个实例对象包含了一个Class类型的isa指针;
接下来看一下Class的定义:
typedef struct objc_class *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;
其实这是为了兼容新老版本的Objective-C,新版本的objc_class优化了内部的数据结构和排列方式,但是基本的内容还是相似(详细见ProjectHeaders/objc_runtime_new.h)。
Class描述了任何一个类包含的基本数据信息,父类、名称、版本、实例大小、实例变量列表、成员方法列表、协议列表、以及用于查询方法的缓存等;
首先需要清除一点,OC里面实例对象、类对象(包括类对象、元类对象和根对象)都是一个对象,明白了这一点,下面的就容易理解了;
OC里面有几个Class的概念:Class、Meta Class(元类)、Root Class(根类);类对象从抽象到具体可以这么解释:
Root Class(根类):类的抽象,即描述了所有类都具备的信息,可以认为就是上述的objc_class;联系到自然界就是代表万物的抽象;所以,显而易见所有元类的根类都是同一个。
Meta Class (元类):即用于描述类层面上的信息,如类方法、类属性等,元类的isa指针指向Root Class;
Class (类):用于描述实例对象层面的基本信息,如实例方法、实例属性等等,Class的isa指针指向Meta Class;
而OC里面获取一个对象isa指针的方法是object_getClass(id):
//objc-class.mm
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
有了上述的理解,下面这个大家都见过的图就比较容易理解了:
有一段时间一直不理解isa和superclass这两种关系之间的区别,所以看不懂为什么每一个元类(Meta Class)的isa指针为什么都指向同一个Root Class(Meta),这样做的目的一方面是为了保证设计的完整性(每个类对象都包含一个isa指针),另一方面也符合面向对象编程分层抽象的原则,只是isa指针和super抽象的思路不一样;
而superclass就是我们通常意义上理解的父类子类继承关系:当向一个实例对象,发送消息时(调用函数),查找的顺序是从当前类对象方法列表里找,如果没有则继续向继承的父类类对象查找方法,即类对象继承自父类的所有实例方法;元类对象则对应类属性和类方法,查找顺序同上,所以元类对象继承所有父类元类对象方法;
可以通过下面一段代码理解:
id obj = [UIResponder class];
NSLog(@"clas - %p", obj);
NSLog(@"meta - %p",object_getClass(obj));
NSLog(@"root - %p", object_getClass(object_getClass(obj)));
NSLog(@"classuper - %p", class_getSuperclass(obj));
NSLog(@"metasuper - %p", class_getSuperclass(object_getClass(obj)));
NSLog(@"rootsuper - %p", class_getSuperclass(object_getClass(object_getClass(obj))));
NSLog(@"---------------------------");
obj = [NSObject class];
NSLog(@"clas - %p", obj);
NSLog(@"meta - %p", object_getClass(obj));
NSLog(@"root - %p", object_getClass(object_getClass(obj)));
NSLog(@"classuper - %p", class_getSuperclass(obj));
NSLog(@"metasuper - %p", class_getSuperclass(object_getClass(obj)));
NSLog(@"rootsuper - %p", class_getSuperclass(object_getClass(object_getClass(obj))));
UIResponder继承自NSObject,所以UIResponder类对象的父类是NSObject类,而UIResponder的元类对象的父类是NSObject的元类,他们元类的根类指向了同一个;运行结果也可以印证这一观点:
2017-10-10 08:47:22.756 SUInterview[9806:3943166] clas - 0x111da7828
2017-10-10 08:47:22.756 SUInterview[9806:3943166] meta - 0x111da7850
2017-10-10 08:47:22.756 SUInterview[9806:3943166] root - 0x10ebc7e38
2017-10-10 08:47:22.756 SUInterview[9806:3943166] classuper - 0x10ebc7e88
2017-10-10 08:47:22.756 SUInterview[9806:3943166] metasuper - 0x10ebc7e38
2017-10-10 08:47:22.757 SUInterview[9806:3943166] rootsuper - 0x10ebc7e88
2017-10-10 08:47:22.757 SUInterview[9806:3943166] ---------------------------
2017-10-10 08:47:22.757 SUInterview[9806:3943166] clas - 0x10ebc7e88
2017-10-10 08:47:22.757 SUInterview[9806:3943166] meta - 0x10ebc7e38
2017-10-10 08:47:22.757 SUInterview[9806:3943166] root - 0x10ebc7e38
2017-10-10 08:47:22.757 SUInterview[9806:3943166] classuper - 0x0
2017-10-10 08:47:22.757 SUInterview[9806:3943166] metasuper - 0x10ebc7e88
2017-10-10 08:47:22.757 SUInterview[9806:3943166] rootsuper - 0x10ebc7e88
同时可以看出,根类的isa指针指向了自己,根类的父类重新指向了基类(NSObject),以及基类(NSObject)没有父类;
[Obj class]和object_getClass(id)的区别
刚接触的时候不是很明白这两个函数的区别,那么直接看一下源码实现:
//NSObject.mm
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
class方法其实分为类方法+ (Class)class和- (Class)class和实例方法,对于实例对象,class方法和object_getClass()方法一样,都是获取isa指针所指的对象;而对于类对象,class方法返回自己;