Objective-C 对象 isa 指针问题

2018-08-13  本文已影响0人  kalpa_shock

OC的对象主要分为三种对象:

1. instance 对象

instance对象是通过类alloc出来的对象, 每次alloc都会产生instance对象
实例对象内存中储存的信息有

为什么储存这些信息呢? 在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;

这三个数据数组,分别从名称可以看到储存这方法列表,属性列表,协议列表.
可以看到类对象在内存中存储的信息有

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类型,储存的信息就不一样了,

image.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
上一篇下一篇

猜你喜欢

热点阅读