美文网首页
Objective-C 对象 isa 指针问题

Objective-C 对象 isa 指针问题

作者: kalpa_shock | 来源:发表于2018-08-13 13:16 被阅读0次

    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
    • 其他
      那么这三种对象之间的关系是,我们用两张图来表示
    image.png
    • 演示文稿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

    相关文章

      网友评论

          本文标题:Objective-C 对象 isa 指针问题

          本文链接:https://www.haomeiwen.com/subject/funhvftx.html