美文网首页Objective-C
iOS:isa与superclass

iOS:isa与superclass

作者: 码小菜 | 来源:发表于2019-11-03 19:05 被阅读0次
    风景

    目录
    一,对象的三种类型
    二,对象的存储信息
    三,isa指针
    四,superclass指针
    五,isa和superclass总结

    一,对象的三种类型

    1,instance对象:每次alloc都会产生新的
    2,class对象:每个类有且只有一个
    3,meta-class对象:每个类有且只有一个

    NSObject *instance1 = [[NSObject alloc] init];
    NSObject *instance2 = [[NSObject alloc] init];
    
    NSLog(@"instance---%p---%p", instance1, instance2);
    
    Class class1 = [NSObject class];
    Class class2 = [instance1 class];
    Class class3 = object_getClass(instance2);
    
    NSLog(@"class---%p---%p---%p", class1, class2, class3);
    
    Class metaClass1 = object_getClass(class1);
    Class metaClass2 = object_getClass(class2);
    
    NSLog(@"metaClass---%p---%p", metaClass1, metaClass2);
    
    // 打印
    instance---0x600002b0c290---0x600002b0c2a0      // 地址不同
    class---0x10c7b6ec8---0x10c7b6ec8---0x10c7b6ec8 // 地址相同
    metaClass---0x10c7b6e78---0x10c7b6e78           // 地址相同
    

    注意:object_getClass的返回值是由参数来定的,传instance对象返回class对象,传class对象返回meta-class对象

    二,对象的存储信息
    存储信息

    1,实例对象的存储信息

    @interface Person : NSObject <NSCopying>
    {
        NSString *_name;
    }
    @property (nonatomic, assign) NSInteger age;
    - (void)eat;
    + (void)run;
    @end
    
    @implementation Person
    - (void)eat {
        NSLog(@"eat");
    }
    + (void)run {
        NSLog(@"run");
    }
    @end
    

    将上述代码用clang转为C++代码,Person的底层代码如下,可以看到实例对象只包含isa指针和成员变量

    struct NSObject_IMPL {
        Class isa;
    };
    
    struct Person_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        NSString *_name;
        NSInteger _age; // 属性的本质是成员变量
    };
    

    2,类对象和元类对象的存储信息(源码下载地址

    • 它们的类型都是ClassClass是指向objc_class结构体的指针,下面是objc_class结构体部分实现代码
    struct objc_object {
        isa_t isa;
    }
    
    struct objc_class : objc_object {
        Class superclass;
        class_rw_t *data() { 
            return bits.data();
        }
    }
    
    struct class_rw_t {
        const class_ro_t *ro;
        method_array_t methods;      // 方法列表
        property_array_t properties; // 属性列表
        protocol_array_t protocols;  // 协议列表
    }
    
    struct class_ro_t {
        const ivar_list_t *ivars; // 成员变量列表
    }
    
    • 运行下面代码来验证一下,Class看不到具体的信息,所以将Class转换为yj_objc_classyj_objc_class就是objc_class,只是把objc_class代码copy出来加了个前缀而已,为了防止命名冲突
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            // 类对象
            yj_objc_class *personClass = (__bridge yj_objc_class *)([Person class]);
            class_rw_t *personClassData = personClass->data();
            // 元类对象
            yj_objc_class *personMetaClass = (__bridge yj_objc_class *)object_getClass([Person class]);
            class_rw_t *personMetaClassData = personMetaClass->data();
        }
        return 0;
    }
    
    类对象 元类对象
    • 总结:类对象和元类对象的结构是一样的,只是存放的数据不一样,类对象的methods里存放是实例方法,元类对象的methods里存放是类方法,元类对象的ivarspropertiesprotocols都为NULL

    • 注意:实例对象和类对象存储的成员变量是不同的,类对象存储的是成员变量的类型(NSInteger),名称(age)等信息,而实例对象存储的是具体值

    三,isa指针

    1,指向

    • 实例对象的isa指针指向类对象
    • 类对象的isa指针指向元类对象
    isa指针

    2,代码验证

    NSObject *instance = [[NSObject alloc] init];
    yj_objc_class *clas = (__bridge yj_objc_class *)[NSObject class];
    Class metaClass = object_getClass([NSObject class]);
    

    由下可知,isa指针中保存的地址进行一次位运算就是对象的地址

    isa指针

    3,作用

    • 当执行[p eat]时,先通过对象p的isa指针找到Person的类对象,然后在Person的类对象中找到eat方法进行调用
    • 当执行[Person run]时,先通过Person类对象的isa指针找到Person的元类对象,然后在Person的元类对象中找到run方法进行调用
    Person *p = [[Person alloc] init];
    [p eat];
    [Person run];
    
    四,superclass指针

    1,指向

    • 类对象的superclass指针指向父类的类对象
    • 元类对象的superclass指针指向父类的元类对象
    类对象的superclass指针 元类对象的superclass指针

    2,代码验证

    yj_objc_class *studentClass = (__bridge yj_objc_class *)[Student class];
    yj_objc_class *personClass = (__bridge yj_objc_class *)[Person class];
    Class objectClass = [NSObject class];
    

    由下可知,superclass指针中保存的地址就是对象的地址

    superclass指针

    3,作用

    • 当执行[s eat]时,先通过对象s的isa指针找到Student的类对象,然后通过Student类对象的superclass指针找到Person的类对象,在Person的类对象中找到eat方法进行调用
    • 当执行[Student run]时,先通过Student类对象的isa指针找到Student的元类对象,然后通过Student元类对象的superclass指针找到Person的元类对象,在Person的元类对象中找到run方法进行调用
    Student *s = [[Student alloc] init];
    [s eat];
    [Student run];
    
    五,isa和superclass总结

    1,查找路径

    • [s copy]的查找路径为下图中的红色箭头
    • [Student load]的查找路径为下图中的蓝色箭头
    // copy是NSObject的实例方法,load是NSObject的类方法
    
    Student *s = [[Student alloc] init];
    [s copy];
    [Student load];
    
    查找路径

    2,特殊点

    • 图中数字1和2:任何类的元类对象的isa指针都指向根类的元类对象
    • 图中数字3:根类的元类对象的isa指针指向自己
    • 图中数字4:根类的类对象的superclass指针指向nil
    • 图中数字5:根类的元类对象的superclass指针指向根类的类对象
    特殊点

    3,验证数字5

    问题:当+方法没有实现时为何会调用-方法?
    答:因为方法调用最终会转换为objc_msgSend进行执行,而objc_msgSend是不区分+-的,只根据方法名进行查找,[Student test]会转换为objc_msgSend([Student class], @selector(test))

    @interface NSObject (Test)
    + (void)test;
    @end
    
    @implementation NSObject (Test)
    - (void)test {
        NSLog(@"-test");
    }
    @end
    
    [Student test]; // 下图是此代码的查找路径
    
    // 打印
    -test
    
    验证数字5

    相关文章

      网友评论

        本文标题:iOS:isa与superclass

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