面试题引发的思考:
Q: OC的类信息存放在哪里?
- 类方法,存放在meta-class对象中;
- 对象方法、属性、成员变量、协议信息,存放在class对象中;
- 成员变量的具体值,存放在instance对象。
Q: 对象的isa
指针指向哪里?
- instance对象的
isa
指向class对象; - class对象的
isa
指向meta-class对象; - meta-class对象的
isa
指向基类的meta-class对象。
1. OC的类信息存放在哪里?
(1) OC对象的分类:
Objective-C对象,简称OC对象,主要分为3种:
- instance对象(实例对象)
- class对象(类对象)
- meta-class对象(元类对象)
1> instance对象(实例对象)
instance对象就是通过类alloc
出来的对象,每次调用alloc
都会产生新的instance对象。
int main(int argc, const char * argv[]) {
@autoreleasepool {
// instance对象,实例对象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
}
return 0;
}
-
object1
、object2
是NSObject
的instance对象(实例对象); - 它们是不同的两个对象,分别占据着两块不同的内存。
instance对象在内存中存储的信息包括:
isa
指针- 其他成员变量
@interface Person : NSObject {
@public
int _age;
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p1 = [[Person alloc] init];
p1->_age = 3;
Person *p2 = [[Person alloc] init];
p2->_age = 4;
}
return 0;
}
2> class对象(类对象)
通过class
方法或runtime方法可以得到class对象:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// instance对象,实例对象
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
// class对象,类对象
// class方法返回的一直是class对象,类对象
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
// runtime
Class objectClass4 = object_getClass(object1);
Class objectClass5 = object_getClass(object2);
// 以下输出地址相同
NSLog(@"%p %p %p %p %p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);
}
return 0;
}
-
objectClass1
~objectClass5
都是NSObject
的class对象; - 它们是同一个对象,每个类在内存中有且只有一个class对象。
class对象在内存中存储的信息主要包括:
isa
指针superclass
指针- 类的属性信息(
property
),类的成员变量信息(ivar
)- 类的对象方法信息(
instance method
),类的协议信息(protocol
)- ......
3> meta-class对象(元类对象)
int main(int argc, const char * argv[]) {
@autoreleasepool {
// meta-class对象,元类对象
// 将类对象当做参数传入,获得元类对象
Class objectMetaClass = object_getClass([NSObject class]);
// 而调用类对象的class方法时得到还是类对象,无论调用多少次都是类对象
Class objectMetaClass2 = [[[NSObject class] class] class];
// 判断该对象是否为元类对象
BOOL result = class_isMetaClass([NSObject class]);
}
return 0;
}
-
objectMetaClass
是NSObject
的meta-class对象; - 每个类在内存中有且只有一个meta-class对象。
meta-class对象在内存中存储的信息主要包括:
isa
指针superclass
指针- 类的类方法信息(
class method
)- ......
- meta-class对象和class对象的内存结构是一样的,但是用途不一样:
- meta-class对象主要存放:类方法
- class对象主要存放:类的属性,类的对象方法、协议、成员变量信息等
- instance对象主要存放:成员变量
补充:objc_getClass
、object_getClass
、class
区别:
1. Class objc_getClass(const char *aClassName)
1> 传入字符串类名
2> 返回对应的类对象
2. Class object_getClass(id obj)
1> 传入的obj可能是instance对象、class对象、meta-class对象
2> 返回值
a) 如果是instance对象,返回class对象
b) 如果是class对象,返回meta-class对象
c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象
3. - (Class)class、+ (Class)class
1> 返回的就是类对象
2. 对象的isa
指针指向哪里?
(1) isa
指针、superclass
指针:
Q: instance对象、class对象、meta-class对象中的isa
指针,有何作用?
Q: class对象、meta-class对象中的superclass
指针又有何作用?
如上图所示:
- instance的
isa
指向class:
当调用对象方法时,通过instance的isa
找到class,最后找到对象方法的实现进行调用;- class的
isa
指向meta-class:
当调用类方法时,通过class的isa
找到meta-class,最后找到类方法的实现进行调用。
1> 对象方法的实现流程
如上图所示, 当Student
的instance对象要调用Person
的对象方法时:
- 首先通过
isa
找到Student
的class; - 然后通过
superclass
找到Person
的class; - 最后找到对象方法的实现进行调用。
2> 类方法的实现流程
如上图所示,当Student
的class对象要调用Person
的类方法时:
- 首先通过
isa
找到Student
的meta-class; - 然后通过
superclass
找到Person
的meta-class; - 最后找到类方法的实现进行调用。
(2) 下图详尽的显示出isa
指针、superclass
指针的指向:
总结可知:
- instance的
isa
指向class- class的
isa
指向meta-class- meta-class的
isa
指向基类的meta-class- class的
superclass
指向父类的class
如果没有父类,superclass
指针为nil
- meta-class的
superclass
指向父类的meta-class
基类的meta-class的superclass
指向基类的class- instance调用对象方法的轨迹
isa
找到class,方法不存在,就通过superclass
找父类- class调用类方法的轨迹
isa
找meta-class,方法不存在,就通过superclass
找父类
(3) 验证:
1> 首先验证object
的isa
指针是否指向objectClass
:
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *object = [[NSObject alloc] init];
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass(objectClass);
NSLog(@"%p %p %p", object, objectClass, objectMetaClass);
}
return 0;
}
通过打断点打印相应对象的isa
指针:
Q: 为什么object
的isa
指针和objectClass
的地址不同呢?
查看OC源码发现:
从64bit开始,
isa
需要进行一次位运算,才能计算出真实地址
那么进行位运算:
object
的isa
指针地址0x001dffff96537141
与0x00007ffffffffff8
进行位运算,得到0x00007fff96537140
,与objectClass
的地址相同;验证完成。
2> 再验证objectClass
的isa
指针是否指向objectMetaClass
:
通过打印objectClass->isa
:
无法获得结果,根据提示进入Class内部查看:
objc_class
结构体内部第一个对象是isa
指针,为了获取isa
指针,我们通过创建一个相同结构的结构体并通过强制转化拿到isa
指针:
struct my_object_class {
Class isa;
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass(objectClass);
struct my_object_class *objectClass2 = (__bridge struct my_object_class *)(objectClass);
}
return 0;
}
那么进行位运算:
objectClass
的isa
指针地址0x001dffff8b42d0f1
与0x00007ffffffffff8
进行位运算,得到0x001dffff8b42d0f0
,与meta-class的地址相同;验证完成。