美文网首页面试宝点
Objective-C的本质

Objective-C的本质

作者: 鼬殿 | 来源:发表于2020-08-03 18:40 被阅读0次

    我们平时编写的Objective-C代码,底层实现其实都是C\C++代码



    所以Objective-C的面向对象都是基于C\C++的数据结构实现的

    思考:Objective-C的对象、类主要是基于C\C++的什么数据结构实现的?
    答案是:结构体

    将Objective-C代码转换为C\C++代码

    xcrun  -sdk  iphoneos  clang  -arch  arm64  -rewrite-objc  OC源文件  -o  输出的.CPP文件
    

    如果需要链接其他框架,使用-framework参数。比如-framework UIKit

    OC源码

    NSObject *obj = [[NSObject alloc] init];
    

    转为C\C++代码

    // NSObject Implementation
    struct NSObject_IMPL {
        Class isa; // 8个字节
    };
    

    苹果源码中Class如下解释
    An opaque type that represents an Objective-C class.

    typedef struct objc_class *Class;
    

    面试题

    一个NSObject对象占用多少内存?
    答案是:
    系统分配了16个字节给NSObject对象(通过malloc_size函数获得)
    但NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)

    验证:
    获得NSObject实例对象的成员变量所占用的大小 >> 8

    #import <objc/runtime.h>
    NSLog(@"%zd", class_getInstanceSize([NSObject class]));
    

    获得obj指针所指向内存的大小 >> 16

    #import <malloc/malloc.h>
    NSLog(@"%zd", malloc_size((__bridge const void *)obj));
    

    实时查看内存数据

    • 读取内存
      memory read/数量格式字节数 内存地址
      x/数量格式字节数 内存地址

    • 修改内存中的值
      memory write 内存地址 数值

    • 格式
      x是16进制,f是浮点,d是10进制

    • 字节大小
      b:byte 1字节,h:half word 2字节
      w:word 4字节,g:giant word 8字节

    (lldb) po obj
    <NSObject: 0x100595420>
    
    (lldb) x 0x100595420
    0x100595420: 19 11 0c 92 ff ff 1d 00 00 00 00 00 00 00 00 00 
    

    进一步探索

    1.声明一个Student类
    @interface Student : NSObject
    {
        @public
        int _no;
        int _age;
    }
    @end
    

    转为C\C++代码

    struct Student_IMPL {
        struct NSObject_IMPL NSObject_IVARS;---8个字节存储空间
        int _no;---4
        int _age;---4
    };
    
    struct NSObject_IMPL {
        Class isa;
    };
    

    验证:

    Student *stu = [[Student alloc] init];
            stu->_no = 4;
            stu->_age = 5;
            
     NSLog(@"%zd", class_getInstanceSize([Student class]));
     NSLog(@"%zd", malloc_size((__bridge const void *)stu));
    

    打印结果

    16
    16

    实时查看内存数据

    (lldb) po stu
    <Student: 0x10283b410>
    
    (lldb) x 0x10283b410
    0x10283b410: c9 11 00 00 01 80 1d 00 04 00 00 00 05 00 00 00
    
    2.声明一个Person类
    @interface Person : NSObject
    {
        int _age;
        int _height;
        int _no;
    }
    @end
    

    转为C\C++代码

    struct NSObject_IMPL {
        Class isa;
    };
    
    struct Person_IMPL {
        struct NSObject_IMPL NSObject_IVARS; 8
        int _age;  4
        int _height; 4
        int _no;  4
    }; 24
    内存对齐:结构体的大小必须是最大成员大小的倍数
    

    验证:

    Person *p = [[Person alloc] init];
    NSLog(@"%zd", sizeof(struct Person_IMPL));  24
    NSLog(@"%zd %zd",class_getInstanceSize([Person class]), 24
                  malloc_size((__bridge const void *)(p))); 32
    

    查看苹果官方源码:https://opensource.apple.com/tarballs/libmalloc/可以看到obj分配的内存的大小都是16的倍数

    #define NANO_MAX_SIZE           256 /* Buckets sized {16, 32, 48, 64, 80, 96, 112, ...} */
    

    OC对象的分类

    Objective-C中的对象,简称OC对象,主要可以分为3种:

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

    instance

    instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象

    instance对象在内存中存储的信息包括

    • isa指针
    • 其他成员变量

    class

     NSObject *object1 = [[NSObject alloc] init];
     NSObject *object2 = [[NSObject alloc] init];
            
     Class objectClass1 = [object1 class];
     Class objectClass2 = [object2 class];
     Class objectClass3 = object_getClass(object1);
     Class objectClass4 = object_getClass(object2);
     Class objectClass5 = [NSObject class];
            
     NSLog(@"%p %p",
                  object1,
                  object2);
            
     NSLog(@"%p %p %p %p %p",
                  objectClass1,
                  objectClass2,
                  objectClass3,
                  objectClass4,
                  objectClass5);
    
    0x10051fac0 0x1005228e0
    0x7fff920c1118 0x7fff920c1118 0x7fff920c1118 0x7fff920c1118 0x7fff920c1118
    

    objectClass1 ~ objectClass5都是NSObject的class对象(类对象)
    它们是同一个对象,每个类在内存中有且只有一个class对象.

    class对象在内存中存储的信息主要包括

    • isa指针
    • superclass指针
    • 类的属性信息(@property)、类的对象方法信息(instance method)
    • 类的协议信息(protocol)、类的成员变量信息(ivar)
      ......
      C\C++源码如下:
    struct _class_ro_t {
        unsigned int flags;
        unsigned int instanceStart;
        unsigned int instanceSize;
        const unsigned char *ivarLayout;
        const char *name;
        const struct _method_list_t *baseMethods;
        const struct _objc_protocol_list *baseProtocols;
        const struct _ivar_list_t *ivars;
        const unsigned char *weakIvarLayout;
        const struct _prop_list_t *properties;
    };
    
    struct _class_t {
        struct _class_t *isa;
        struct _class_t *superclass;
        void *cache;
        void *vtable;
        struct _class_ro_t *ro;
    };
    

    meta-class

    Class objectMetaClass = object_getClass([NSObject class]);
    

    objectMetaClass是NSObject的meta-class对象(元类对象)
    每个类在内存中有且只有一个meta-class对象

    meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括

    • isa指针
    • superclass指针
    • 类的类方法信息(class method)
      ......

    以下代码获取的objectClass是class对象,并不是meta-class对象

    Class objectClass = [[[NSObject class] class] class]
    

    查看Class是否为meta-class

    BOOL result = class_isMetaClass([NSObject class])
    
    1.Class objc_getClass(const char *aClassName)

    1> 传入字符串类名
    2> 返回对应的类对象

    2.Class object_getClass(id obj)

    1> 传入的obj可能是instance对象、class对象、meta-class对象
    2> 返回值
    a) 如果是instance对象,返回class对象
    b) 如果是class对象,返回meta-class对象
    c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象

    3.- (Class)class、+ (Class)class

    返回的就是类对象

    - (Class) {
        return self->isa;
    }
    
    + (Class) {
        return self;
    }
    

    isa指针

    • instance的isa指向class
      当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用

    • class的isa指向meta-class
      当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用

    从64bit开始,isa需要进行一次位运算& ISA_MASK,才能计算出真实地址.
    define ISA_MASK 0x0000000ffffffff8ULL

    class对象的superclass指针

    @interface Student : Person
    @interface Person : NSObject
    

    当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用

    meta-class对象的superclass指针

    当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用

    isa、superclass总结

    • instance的isa指向class

    • class的isa指向meta-class

    • meta-class的isa指向基类的meta-class

    • class的superclass指向父类的class
      如果没有父类,superclass指针为nil

    • meta-class的superclass指向父类的meta-class
      基类的meta-class的superclass指向基类的class

    • instance调用对象方法的轨迹
      isa找到class,方法不存在,就通过superclass找父类

    • class调用类方法的轨迹
      isa找meta-class,方法不存在,就通过superclass找父类

    窥探struct objc_class的结构

    苹果开源源码https://opensource.apple.com/tarballs/objc4/

    struct objc_object {
    private:
        isa_t isa;
    }
    
    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
    }
    
    struct class_rw_t {
       ......
    private:
        using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;
    }
    struct class_rw_ext_t {
        const class_ro_t *ro;
        method_array_t methods;  //方法列表
        property_array_t properties; //属性列表
        protocol_array_t protocols; //协议列表
        char *demangledName;
        uint32_t version;
    };
    
    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize; //instance对象z占用的内存空间
    #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;
    }
    
    

    面试题:

    对象的isa指针指向哪里?

    • instance对象的isa指向class对象
    • class对象的isa指向meta-class对象
    • meta-class对象的isa指向基类的meta-class对象

    OC的类信息存放在哪里?

    • 对象方法、属性、成员变量、协议信息,存放在class对象中
    • 类方法,存放在meta-class对象中
    • 成员变量的具体值,存放在instance对象

    相关文章

      网友评论

        本文标题:Objective-C的本质

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