美文网首页
OC对象的本质(二)

OC对象的本质(二)

作者: comsubin | 来源:发表于2019-05-22 10:56 被阅读0次

    OC 对象有三种

    • instance 实例对象
    • class 类对象
    • meta-class 元类

    实例对象

    通过alloc出来的对象都是实例对象,而且由于它们都继承自NSObject所以他们都有一个class isa的成员变量.那么实例对象在内存中存储的信息就是:

    • isa 指针
    • 其他成员变量

    类对象

    image.png

    一个类对象在内存中只有一份,存储信息:

    • isa指针
    • superclass 指针
    • 类的属性信息(@property)
    • 类的对象方法信息(instance方法)
    • 类的协议信息(protocol)
    • 类的成员变量信息(ivar)

    元类

    image.png
    注意:这里只能用runtime的方式获取元类,而调用class方法不论多少次都是只返回类对象.都是Class类型结构一样.
    每个类在内存有且只有一个元类对象,存储信息主要包括
    • isa指针
    • superclass 指针
    • 类的类方法(class method)

    先来看一张图:

    image.png
    instanceisa指向class
    • 当调用对象方法的时候,通过instanceisa找到class,最后找到对象方法进行实现.
      class类对象的isa指向元类,
    • 当调用类方法的时候,通过类对象的isa找到元类,最后找到类方法进行实现.
      superclass 是在调用父类的方法用到的.
      为什么说instanceisa指向class的呢?下面来简单验证下:
    @interface father :NSObject
    
    @end
    
    @implementation father
    
    
    @end
    
    
    @interface son : father
    
    
    @end
    
    @implementation son
    
    
    
    @end
    
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            
            son *sonInstacne = [[son alloc]init];
            Class sonClass = [son class];
            Class metaClass = object_getClass(sonClass);
            NSLog(@"%p --%p---%p",sonInstacne,sonClass,metaClass);
            return  0;
     }
    }
    

    断点调试:这里注意一下64位环境下isa要通过& ISAMASK运算才能算出真正的地址:

    image.png
    ISAMASK是个宏,通过runtime源码发现:
    # if __arm64__
    #   define ISA_MASK        0x0000000ffffffff8ULL
    #   define ISA_MAGIC_MASK  0x000003f000000001ULL
    #   define ISA_MAGIC_VALUE 0x000001a000000001ULL
        struct {
            uintptr_t nonpointer        : 1;
            uintptr_t has_assoc         : 1;
            uintptr_t has_cxx_dtor      : 1;
            uintptr_t shiftcls          : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
            uintptr_t magic             : 6;
            uintptr_t weakly_referenced : 1;
            uintptr_t deallocating      : 1;
            uintptr_t has_sidetable_rc  : 1;
            uintptr_t extra_rc          : 19;
    #       define RC_ONE   (1ULL<<45)
    #       define RC_HALF  (1ULL<<18)
        };
    
    # elif __x86_64__
    #   define ISA_MASK        0x00007ffffffffff8ULL
    #   define ISA_MAGIC_MASK  0x001f800000000001ULL
    #   define ISA_MAGIC_VALUE 0x001d800000000001ULL
        struct {
            uintptr_t nonpointer        : 1;
            uintptr_t has_assoc         : 1;
            uintptr_t has_cxx_dtor      : 1;
            uintptr_t shiftcls          : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000
            uintptr_t magic             : 6;
            uintptr_t weakly_referenced : 1;
            uintptr_t deallocating      : 1;
            uintptr_t has_sidetable_rc  : 1;
            uintptr_t extra_rc          : 8;
    #       define RC_ONE   (1ULL<<56)
    #       define RC_HALF  (1ULL<<7)
        };
    
    image.png
    这里可看出sonInstacne的isa是指向 sonclass的.接下来分析sonclass的isa是否指向metaclass:
    image.png
    备注:p/x print 输出 x 输出十六进制.po = print object
    再来看网上的一张图:
    image.png
    上图分析可知:
    • 实例对象instanceisa是指向类对象class
    • 类对象的isa是指向元类对象meta-class
    • 元类对象的isa是指向基类root class
    • 类对象的superclass是指向父类的class
    • 基类的元类superclass是指向基类的class(这个比较特殊)
      小结:
      实例方法调用流程:
    • isa 找到class,方法不存在,通过superclass找到父类,最后到基类的class也找到不到,抛出异常.
      类对象方法调用流程:
    • isameta-class,方法不存在,通过superclass找,最后基类的元类会去其类对象中找.(下面举例说明)
      新建一个person类,.m文件不实现该方法.
    @interface Person : NSObject
    + (void)test;
    
    @end
    

    新建NSObject分类

    @interface NSObject (test)
    
    + (void)test;
    
    @end
    

    .m文件实现用实例方法

    @implementation NSObject (test)
    
    //+ (void)test{
    //    NSLog(@"NSObject-test---%p",self);
    //}
    - (void)test{
        NSLog(@"这是实例方法");
    }
    
    @end
    
    image.png

    person类对象通过isa找到元类对象,在里面没有发现方法,前去superclass找,基类的元类对象也没找到,就去该基类的类对象找,(而这里是存放实例方法的).为什么会这样?
    这里涉及到runtime相关,先知道是这么回事就行.后面在补充runtime.
    补充:Class 结构分析

    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
        cache_t cache;             // 方法缓存
        class_data_bits_t bits;    // 用于获取具体的类信息
    }
    
        class_rw_t* data() {
            return (class_rw_t *)(bits & FAST_DATA_MASK);
        }
    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;
    }
    
    
    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize;//占用空间
    #ifdef __LP64__
        uint32_t reserved;
    #endif
    
        const uint8_t * ivarLayout;
        
        const char * name;//类名
        method_list_t * baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars;//成员变量列表
    
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
    
        method_list_t *baseMethods() const {
            return baseMethodList;
        }
    };
    

    面试题:
    1,对象的isa指向哪里?

    • 实例对象的isa指向class,class的isa指向meta-class,元类的isa指向基类的meta-class.

    2,类信息存放在哪里?

    • 对象方法,属性,成员,协议存放在class里面,
    • 类方法存放在元类里面,
    • 成员变量的具体值是存放在instance里面

    相关文章

      网友评论

          本文标题:OC对象的本质(二)

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