Objective-C 对象 isa 指针问题
OC的对象主要分为三种对象:
- instance 实例对象
- class 类对象
- meta-class元类对象
1. instance 对象
instance对象是通过类alloc出来的对象, 每次alloc都会产生instance对象
实例对象内存中储存的信息有
- isa指针
- 其他成员变量
为什么储存这些信息呢? 在OC对象本质里面可以看到实例对象的分配内存大小,储存的信息为实例对象的值
那么类的方法储存到哪里了呢?
接下来看class 类对象
2. class 类对象
获得一个类的类对象有很多种方式
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
NSObject *obj1 = [[NSObject alloc] init];
NSLog(@"obj1 --- %@ --- %p",[obj1 class],[obj1 class]);
NSLog(@"obj1 --- %@ --- %p",object_getClass(obj1),object_getClass(obj1));
NSLog(@"obj --- %@ --- %p",[obj class],[obj class]);
NSLog(@"obj --- %@ --- %p",object_getClass(obj),object_getClass(obj));
NSLog(@"NSObject --- %@ --- %p",[NSObject class],[NSObject class]);
}
return 0;
}
2018-08-03 01:48:29.032955+0800 oc[32142:1592863] obj1 --- NSObject --- 0x7fff99f79140
2018-08-03 01:48:29.033277+0800 oc[32142:1592863] obj1 --- NSObject --- 0x7fff99f79140
2018-08-03 01:48:29.033352+0800 oc[32142:1592863] obj --- NSObject --- 0x7fff99f79140
2018-08-03 01:48:29.033372+0800 oc[32142:1592863] obj --- NSObject --- 0x7fff99f79140
2018-08-03 01:48:29.033390+0800 oc[32142:1592863] NSObject --- NSObject --- 0x7fff99f79140
Program ended with exit code: 0
从打印结果可以看出类对象在内存中只存在一份,
类对象的类型是Class类型也就是typedef struct objc_class *Class;
类型,那么这个Class是一个指向typedef struct objc_class
结构体的指针.
下面这是objc的源码
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
#if SUPPORT_INDEXED_ISA
uint32_t index;
#endif
... //因为下面都是方法,只看这一些就够了
};
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
... //因为下面都是方法,只看这一些就够了
因为是C++代码, 可以看到objc_class继承于objc_object结构体
objc_object这个结构体里面只有一个变量就是isa指针.
其中data()函数是返回一个class_rw_t *
类型的结构体指针,这个结构体中存在着
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
这三个数据数组,分别从名称可以看到储存这方法列表,属性列表,协议列表.
可以看到类对象在内存中存储的信息有
- Class isa;
- Class superclass;
- class_data_bits_t bits;
其中bits中储存了 - const class_ro_t *ro;
- method_array_t methods; //类的实例方法信息
- property_array_t properties;//类的属性信息(比如:类型,名字)
- protocol_array_t protocols;//类的协议信息
- 其他
可以看到类对象中储存这只需要一份内存的东西
3. 元类对象
元类对象的获取
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
NSLog(@"%p",object_getClass([NSObject class]));
NSLog(@"%p",object_getClass(object_getClass(obj)));
}
return 0;
}
2018-08-03 01:55:15.300574+0800 oc[32241:1604313] 0x7fff99f790f0
2018-08-03 01:55:15.300903+0800 oc[32241:1604313] 0x7fff99f790f0
Program ended with exit code: 0
可以知道元类对象的结构是和类对象一样都是Class类型,储存的信息就不一样了,
- Class isa;
- Class superclass;
- class_data_bits_t bits;
其中bits中储存了 - const class_ro_t *ro;
- method_array_t methods; //类的类方法信息
- property_array_t properties; //nil
- protocol_array_t protocols; //nil
- 其他
那么这三种对象之间的关系是,我们用两张图来表示
- 演示文稿1 [自动保存].png
第二张图是对第一张图的解释,其中元类对象比较特殊,其中
@interface Animal : NSObject
{
int _count;
}
@end
@implementation Animal
- (void)eat
{
NSLog(@"Animal eat");
}
@end
@interface Cat : Animal
{
int _weight;
}
@end
@implementation Cat
- (void)eat
{
NSLog(@"Cat eat");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Cat *cat = [[Cat alloc] init];
[cat eat];
}
return 0;
}
2018-08-03 16:47:28.821773+0800 oc[45490:2208389] Cat eat
Program ended with exit code: 0
cat这个对象调用eat方法实际上是对cat对象发送了eat消息
相当于就是调用了objc_msgSend(Class cls,SEL)
这个函数
那我们看下它是如何找方法的
首先他会通过这个实例对象cat的isa指针找到这个类的类对象, 在类对象里面查找是否存在eat方法,存在则调用,不存在它会通过superclass指针找到父类的类对象,找类对象储存的方法列表中是否存在eat方法, 若存在则调用, 如不存在则再往上找,知道找到NSObject方法,如果不存在则报异常unrecognized selector sent to instance 0x10045ade0'
这个错误
同理调用类方法会通过类队形的isa指针找到这个类对象的元类对象,在元类对象存储的方法列表中找方法, 找到调用,找不到通过superclass指针找到父类的元类对象, 再次找方法,找不到一直找到NSObject的元类对象,
NSObject的元类对象有些特殊,其中superclass指针指向的是NSObject的类对象, 如果NSobject的元类对象没有该方法,会通过superclass指针找到NSObject的类对象,寻找方法
objc_msgSend()函数找方法流程图:
对象方法调用流程.png
类方法调用流程.png