美文网首页
聊一聊对象(一)

聊一聊对象(一)

作者: 晨阳Xia | 来源:发表于2020-12-16 16:08 被阅读0次

    Apple源码

    iOS开发之OC对象本质

    一、相关内容

    1.1LLDB 知多少

    1.2 读取函数寄存器

    register read x0 : 函数的第一个参数
    register read x1 : 函数的第二个参数
    memory read : 读取内存

    二、alloc的流程

    案例代码:

    @interface Person : NSObject
    
    @property(assign, nonatomic) int age;
    
    @end
    
    Person *p = [Person alloc]; // objc_msgSend(Person, SEL(alloc));
    

    2.1 alloc做了什么:(源码分析)

    alloc已经生成了对象,并返回对象 obj

    p.age = 10;
    NSLog(@"p.age--%ld",(long)p.age);
    p.age--:10 // 没有发送init消息,依然可以创建p对象,并进行赋值操作。
    
    // alloc内部实现
    +(id)alloc {
        return _objc_rootAlloc(self);
    }
    
    _objc_rootAlloc(Class cls) {
        return callAlloc(cls, false/*checkNil*/,true/*allocWithZone*/)
    }
    
    callAlloc(Class cls, bool checkNil, bool allocWithZone = false) {  
        ...
        id obj = class_createInstance(cls, 0);
        ...
        return obj;
    }
    
    

    到此为止,alloc完成了创建对象对的过程。

    2.2 init的流程(源码分析)

    p1 = [p init]; // objc_msgSend(p, SEL(init));
    NSLog(@"p1.age--%ld",(long)p1.age);
    p1.age--:10 
    
    - (id)init {
        return _objc_rootInit(self);
    }
    _objc_rootInit(id obj) {
        // In practice ,it will be hard to rely on this function
        // Many class do not properly chain -init calls
        return obj;
    }
    

    2.3 init做了什么:

    直接return obj。init相当于一个工厂类,是为了让重写它,实现自己的方法。既然alloc已经生成了对象,是不是我们也可以不写init?不可以。因为Foundation框架在init里做了必要工作。如果没有init,那么你创建就是一个没有任何功能的对象。

    三、什么是字节对齐?为什么要字节对齐?

    3.1 字节对齐:

    iOS在arm中按照8字节对齐,开辟的内存一定是8的倍数。比如我需要9个字节,那我开辟的自己是 16个字节。字节对齐一定是浪费空间的。

    3.2 为什么要字节对齐:

    因为硬件决定的。有些cpu只能从偶数开始读数据。提高读取效率。
    代码对齐的源码:

    WORD_MASK = 7;
    int word_align(int x) {
        // (x + 7) / 8 * 8 
        // (x + 7) >> 3 << 3
        return (x + WORD_MASK) & ~WORD_MASK; 
    }
    

    字节对齐案例:

    @interface Person : NSObject
    
    @property(assign, nonatomic) int age;
    @property(assign, nonatomic) double height;
    
    @end
    
    Person *p = [Person alloc];
    p.age = 1;
    p.height = 1.85;
    
    

    p共有 24个字节
    age : 4个字节
    height : 8 个字节
    isa : 8个字节
    4 + 8 + 8 = 20
    因为iOS在arm中按照8字节对齐,所以p供分配了 24个字节

    p中存在三个指针:
    0x100f4fd90: isa: 8字节 age: 4字节
    0x100f4fda0: height: 8字节
    16进制代码如下:
    0x100f4fd90: c1 11 00 00 01 80 1d 00 00 00 00 01 00 00 00 00
    0x100f4fda0: 9a 99 99 99 99 99 fo 3f
    打印double地址:
    p *(double *)0x100f4fda0
    // (double) $1 = 1.8500000000000001

    3.3 结构体的大小必须是最大成员大小的倍数

    @interface CustomPerson : NSObject{
        int _age;
    }
    @end
    
    @implementation CustomPerson
    @end
    
    @interface Student : CustomPerson{
        int _no;
    }
    @end
    
    @implementation Student
    
    @end
    Student *student = [[Student alloc] init];
    NSLog(@"%zu", class_getInstanceSize([Student class]));
    NSLog(@"%zu", malloc_size((__bridge const void *)student));
    2020-12-16 15:32:53.592040+0800  16
    2020-12-16 15:32:53.592230+0800  16
    
    

    将student中的_no换成double类型

    @interface CustomPerson : NSObject{
        int _age;
    }
    @end
    
    @implementation CustomPerson
    @end
    
    @interface Student : CustomPerson{
        double _no;
    }
    @end
    
    @implementation Student
    
    @end
    
    Student *student = [[Student alloc] init];
    NSLog(@"%zu", class_getInstanceSize([Student class]));
    NSLog(@"%zu", malloc_size((__bridge const void *)student));
    2020-12-16 15:34:28.133872+0800 24
    2020-12-16 15:34:28.134047+0800 32
    

    将Person 和 Student转换为结构体
    // int _no

    struct Person_IMPL {
        struct NSObject_IMPL NSObject_IVARS; // 8
        int _age; // 4
    }
    
    struct Student_IMPL {
        struct Person_IMPL NSObject_IVARS; // 16
        int _no; // 4
    }
    因为 NSObject_IVARS 只使用了 12个字节,所以还剩4个字节,直接将_age赋值过去。所以实例变量的占用内存大小是16,系统分配给对象的内存大小也是16
    

    // double _no

    struct Person_IMPL {
        struct NSObject_IMPL NSObject_IVARS; // 8
        int _age; // 4
    }
    
    struct Student_IMPL {
        struct Person_IMPL NSObject_IVARS; // 16
        double _no; // 8
    }
    因为 NSObject_IVARS 只使用了 12个字节,所以还剩4个字节,剩下的自己不足以分配8。又因为结构体的大小必须是最大成员大小的倍数,所以实例变量的占用内存大小是16 + 8 = 24,系统分配给对象的内存大小是 16 * 2 = 32。
    

    四、什么是对象:

    Objective-C对象是结构体
    NSObject转化为C/C++代码源码:

    struct NSObject_IMPL {
        Class isa; // 8个字节
    };
    因为alloc的时候最小分配16字节,所以系统为一个对象分配了16字节,NSObject对象仅占用8字节。
    

    一个指针占用八个字节
    一个空的Objective-C对象被创建出来占用8个字节
    struct objc_object {
    Class *isa;
    } id;

    将代码编译成c++代码:
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc "Class.m" -o "Class.cpp"

    命令 解释
    xcrun Xcoderun
    -sdk 选择支持平台
    iphoneos iphone平台
    clang llvm编辑器命令
    -arch 架构
    arm64 模拟器(i386) 32bit(arm32) 64bit(arm64)
    -rewrite-objc 重写
    -o 保存到某个文件

    实例对象里只放成员变量,不放方法。方法是放在类的方法列表里。

    相关文章

      网友评论

          本文标题:聊一聊对象(一)

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