1.Objective-C中的类
根据面向对象的基本概念,类是对象的抽象,对象是类的具体化,但是在Object-C中,有所区别。 在oc中,类(Class)同样是对象的一部分,每一个对象都有一个指针(isa)指向一个类。一个对象的类由它的isa所指向的Class决定。 我们可以看一下oc中id的定义, objc_object的定义
/// A pointer to an instance of a class.
typedef struct objc_object *id;
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
也就是说,一个结构体,如果它以一个指向类(Class)的指针(isa)开始,我们就可以认为它是一个objc_object, 也就是一个id类型,也就是一个类的对象。 在oc中,对象最重要的特性就是:对象可以接受消息。在我们让一个实例对象调用方法的时候,其中的过程其实是这样的。你发送一个消息给一个Objective-C对象,运行时会根据该对象的isa指针找到他的类(Class),这个Class包含了一个所有类的对象都可以调用的方法列表和一个指向父类的指针用于查找继承的方法。运行时遍历类和父类的方法列表寻找和message selector匹配的方法。运行时会唤起对应的IMP。 重点在于,Class定义了你可以向对象发送哪些消息。
2.Meta-Class
我们都知道,类的方法分为实例方法和类方法,根据我们上面的理论,实例方法在调用的时候是我们发送了消息给实例对象,那类方法在调用的时候,消息发送给了谁呢?对,在Objective-C中,Class也是一个对象,所以我们可以给类发送消息,我们可以调用类方法而不需要创建实例。
在这同时,也意味着Class对象也必须有一个isa指针指向它自己的类,这样才能在二进制层面和我们上面说的id对象进行兼容。我们可以看一下Class的定义,objc_class的定义
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
根据我们之前所知道的方法调用过程,如果一个Class可以调用方法,那么他的isa指针必然会指向一个Class,这个Class(Class的isa指向的Class)中会包含一个它的实例可以调用的方法列表,这个Class的isa指向的Class就是元类(Meta-class)。
总结一下:当我们向一个id对象发送消息时,运行时会在这个对象的Class的方法列表中查找对应的IMP
当我们向一个Class发送消息时,运行时会在这个Class的meta-class的方法列表中查找对应的IMP,meta-Class中存储着这个类的类方法列表。
3.Meta-class的类
注意看objc_class的定义,可以看到Class的isa指针也是指向一个Class类型的结构体,meta-class也是一种Class。而且,meta-Class也是一种object ,这意味着你也可以对它发送消息,这也就意味着,meta-class也会有他自己的类。当然,这不会是无止境的。 所有的meta-class以它的基类的meta-class作为他们的类,任何基类的meta-class的isa将会指向他们自己,比如NSObject的isa指针就指向他们自己。我们可以写一段代码验证一下:
NSString *testStr1 = @"xixi"; NSLog(@"testStr1 内存地址:%p 指针地址%p", testStr1, &testStr1); Class currentClass = object_getClass(testStr1); do { NSLog(@"currentClass now -> %@ 内存地址:%p 指针地址%p", currentClass, currentClass, ¤tClass); currentClass = object_getClass(currentClass); } while(currentClass != NULL);
控制台的结果如下(当然这是个死循环,我们看前几个log找到规律就行了):
2015-11-13 17:42:59.187 Testisa[7029:204141] testStr1 内存地址:0x1000042d0 指针地址0x7fff5fbff808
2015-11-13 17:42:59.189 Testisa[7029:204141] currentClass now -> __NSCFConstantString 内存地址:0x7fff7ede7740 指针地址0x7fff5fbff800
2015-11-13 17:43:14.452 Testisa[7029:204141] currentClass now -> __NSCFConstantString 内存地址:0x7fff7ede76f0 指针地址0x7fff5fbff800
2015-11-13 17:43:23.319 Testisa[7029:204141] currentClass now -> NSObject 内存地址:0x7fff7e39c118 指针地址0x7fff5fbff800
2015-11-13 17:43:25.538 Testisa[7029:204141] currentClass now -> NSObject 内存地址:0x7fff7e39c118 指针地址0x7fff5fbff800
2015-11-13 17:43:26.237 Testisa[7029:204141] currentClass now -> NSObject 内存地址:0x7fff7e39c118 指针地址0x7fff5fbff800
2015-11-13 17:43:26.853 Testisa[7029:204141] currentClass now -> NSObject 内存地址:0x7fff7e39c118 指针地址0x7fff5fbff800
2015-11-13 17:43:28.086 Testisa[7029:204141] currentClass now -> NSObject 内存地址:0x7fff7e39c118 指针地址0x7fff5fbff800
4.Class和Meta-Class的继承
和 Class 以 super_class 指针指向它的父类的方法一样,meta-class 以 super_class 指针指向 Class 的 super_class 的 meta-class。这句话的前半段好理解,后半断我是这样理解,一个Class的meta-class的super_class指针跟他的super_class的isa指针指向的是同一个对象。 由此我们更可以得出结论:基类的meta-class和super_class都指向他自己。由此可以推论,所有在这个继承层次中的实例,类和meta-class都继承了基类的层次。对于所有在NSObject层次中的实例,类和meta-class,所有的NSObject的实例方法都是有效的,对于Class和meta-class,所有NSObject的类方法都是有效的。关于这一点,我们可以看这张图:
结论:
meta-class是Class对象的类(Class的isa指针指向它),每个Class都有不同的自己的meta-class(即使它们的方法列表相同)。也就是说每个类的Class完全不同。
meta-class 总是会保证 Class 对象会有从基类继承的所有的的实例和类方法,加上之后继承的类方法。如从 NSObject 继承的类,就意味着在所有的 Class(和 meta-class)对象中定义了所有从 NSObject 继承的实例和协议方法。所有的 meta-class 使用基类的 meta-class(NSObject 的 meta-class 用于继承自 NSObject 的类)作为他们自己的类,包括在运行时自己定义的基础的 meta-class。
本文大部分思路来自:What is a meta-class in Objective-C?