Objective-C中的对象,简称OC对象,主要可以分为3种
instance对象(实例对象)
class对象(类对象)
meta-class对象(元类对象)
instance
instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
NSObject *obj1 = [[NSObject alloc] init]; NSObject *obj2 = [[NSObject alloc] init]; NSLog(@"%p", obj1); NSLog(@"%p", obj2);
打印结果会输出
0x1004992a0 0x1004974f0
由此可以看出,obj1和obj2是两个不同的对象,分别占据着两块不同的内存。
而instance对象在内存中存储的信息包括
isa指针
成员变量的值_age = 5,这个5就是存在于实例对象中的
Class 对象
查看以下代码中的内存地址
NSObject *object1 = [[NSObject alloc] init]; NSObject *object2 = [[NSObject alloc] init]; Class objectClass1 = [object1 class]; Class objectClass2 = [object2 class]; Class objectClass3 = [NSObject class]; Class objectClass4 = object_getClass(object1); Class objectClass5 = object_getClass(object2);
在控制台调试打印地址
(lldb) p/x (long)objectClass1 (long) $2 = 0x00007fff8a20f140 (lldb) p/x (long)objectClass2 (long) $3 = 0x00007fff8a20f140 (lldb) p/x (long)objectClass3 (long) $4 = 0x00007fff8a20f140 (lldb) p/x (long)objectClass4 (long) $5 = 0x00007fff8a20f140 (lldb) p/x (long)objectClass5 (long) $6 = 0x00007fff8a20f140
经过调试可以发现5个Class类指向同一个地址值0x00007fff8a20f140,它和instance对象的区别是instance对象是alloc分配的内存空间,每个实例对象都占用不同的空间,但是Class一个类只占用一份内存空间。
objectClass1~objectClass5都是NSObject的Class对象(类对象)
它们是同一个对象,每个类在内存中只有一个Class对象
Class对象在内存中存储的信息主要包括
isa指针
superclass指针
类的属性信息(@property)
类的对象方法信息(instance method)
类的协议信息(protocol)
类的成员变量信息(ivar)
meta-class
class和meta-class内存地址值比较
Class objectClass = [NSObject class]; Class objectMetaClass = object_getClass([NSObject class]); NSLog(@"%p", objectClass); NSLog(@"%p", objectMetaClass);
0x7fff8a20f140 0x7fff8a20f0f0
对比发现,Class和meta-class的内存地址不一样。我们可以进行如下总结 :
objectMetaClass是NSObject的meta-class对象(元类对象)
每个类在内存中有且只有一个meta-class对象
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存总存储的主要信息包括
isa指针
superclass指针
类的类方法信息(class method)
注意
获取元类的内存地址只能通过object_getClass([NSObject class])进行获取,通过[[NSObject class] class]这种获取方法是错误的。
Class objectClass = [NSObject class]; Class objectWrongMetaClass = [[NSObject class] class]; Class objectMetaClass = object_getClass([NSObject class]); NSLog(@"%p", objectClass); NSLog(@"%p", objectWrongMetaClass); NSLog(@"%p", objectMetaClass);
0x7fff8a20f140 0x7fff8a20f140 0x7fff8a20f0f0
OC 的类信息存放在哪里?
成员变量具体的值,放在instance(实例对象)里。
属性信息、对象方法信息、协议信息、成员变量信息,放在Class(类对象)里。
类方法信息,放在meta-class(元类对象)里。
对象的 isa 指针指向哪里?
根据上面可知,对象方法存储在class的内存里,类方法存在于meta-class内存里。问题来了,假如现在有一个Person类的实例化对象p1,如果想用p1调用Person类的对象方法personMethod该如何调用呢?毕竟,p1是存储在实例化对象instance内存中的,而personMethod方法是存储于Person类的内存中的。
实际上instance实例的对象的isa指针指向class,找到class类以后,再在class类中找存储于其中的对象方法方法进行调用。
调用类方法的过程也是如此,class类通过其内部的isa指针找到meta-class类中存储的类方法,然后再进行调用。
至此,就可以回答上面的问题了。
instance对象的isa指向class。
当调用对象方法时,通过instance的isa找到class,再找到对象方法进行调用。
class对象的isa指向meta-class。
当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用。
meta-class对象的isa指向基类的meta-class对象。
class 对象的 superclass 指针
有如下两个类,继承关系如下 :
Student->Person->NSobject
@interface Person : NSObject - (void)personMethod; + (void)personClassMethod; @end @implementation Person - (void)personMethod {}; + (void)personClassMethod {}; @end
@interface Student : Person - (void)studentMethod; + (void)studentClassMethod; @end @implementation Student - (void)studentMethod {}; + (void)studentClassMethod {}; @end
创建一个实例student
Student *student = [[Student alloc] init];
调用[student studentMethod]方法的过程
- (void)studentMethod;方法存在于Student的class内部。
通过student(实例对象)的isa指针找到Student的class。
在Student的class内部找到- (void)studentMethod;方法并调用。
调用[student personMethod]方法的过程
- (void)personMethod;方法存在于Person的class内部。
通过student(实例对象)的isa指针找到Student的class。
在Student的class内部找是否存在- (void)personMethod;方法。
不存在,则通过Student的class内的superclass找到Person的class。
在Person的class内找是否存在- (void)personMethod;方法。
存在->调用方法。
调用[student init]方法的过程
- (void)init;方法存在于NSObject的class内部。
通过student(实例对象)的isa指针找到Student的class。
在Student的class内部找是否存在- (void)init;方法。
不存在,则通过Student的class内的superclass指针找到Person的class。
在Person的class内找是否存在- (void)init;方法。
不存在,则通过Person的class内的superclass指针找到NSObject的meta-class。
在NSObject的class内找是否存在- (void)init;方法;
存在->调用方法。
meta-class 对象的 superclass 指针
有如下两个类,继承关系如下 :
Student->Person->NSobject
@interface Person : NSObject - (void)personMethod; + (void)personClassMethod; @end @implementation Person - (void)personMethod {}; + (void)personClassMethod {}; @end
@interface Student : Person - (void)studentMethod; + (void)studentClassMethod; @end @implementation Student - (void)studentMethod {}; + (void)studentClassMethod {}; @end
调用[Student studentClassMethod];方法的过程
+ (void)studentClassMethod;方法存储在Student的meta-class内。
通过Student类对象的isa找到Student的meta-class。
在Student的meta-class内找类方法+ (void)studentClassMethod;并调用。
调用[Student personClassMethod];方法的过程
+ (void)personClassMethod;方法存储在Person的meta-class内
通过Student类对象的isa找到Student的meta-class。
Student的meta-class中不存在+ (void)personClassMethod;方法。
通过Student的meta-class内的superclass找到Person的meta-class。
在Person的meta-class方法内找是否存在+ (void)personClassMethod;方法。
找到方法->调用。
调用[Student load];方法的过程
+ (void)load;是NSObject的类方法。
通过Student类对象的isa找到Student的meta-class。
Student的meta-class中不存在+ (void)load;方法。
通过Student的meta-class内的superclass找到Person的meta-class。
在Person的meta-class方法内找是否存在+ (void)load;方法。
Person的meta-class内不存在+ (void)load;方法。
通过Person的meta-class内的superclass找到NSObject的meta-class。
在NSObject的meta-class内找是否存在+ (void)load;方法。
找到方法->调用。
isa、superclass 总结
下面是一张广为流传关于isa、superclass的经典图。
isa
instance的isa指向class
class的isa指向meta-class
meta-class的isa指向基类的meta-class
superclass
class的superclass指向父类的class
如果没有父类,superclass指针为nil
meta-class的superclass指向父类的meta-class
基类的meta-class的superclass指向基类的class
instance 调用对象方法的轨迹
isa找到class,方法不存在,就通过superclass找父类。
class 调用类方法的轨迹
isa找meta-class方法不存在,就通过superclass找父类。