Objective-C 对象模型深入理解

0x00序

本着加深对Objective-C 对象模型的理解和记忆目的,于是有了下文的简单实践操作。

本文同步Demo

0x01 疑问

在以下代码中,你能描述清楚以下问题吗?

  1. TestClass的实例对象tcAtcB的内存结构是怎么样的
  2. TestClass的实例对象的大小
@interface TestClass : NSObject
{
    @public
    int myInt;
    NSString *name;
}
@property (nonatomic, assign) NSInteger age; ///< 年龄
@end

@implementation TestClass
@end


int main(int argc, char * argv[]) {
    @autoreleasepool {
    //创建实例tcA
    TestClass *tcA = [[TestClass alloc] init];
    tcA->myInt = 100;
    tcA.age = 7;
    tcA->name = @"a";

    //创建实例tcB
    TestClass *tcB = [[TestClass alloc] init];;
    tcB->myInt = 101;
    tcB.age = 5;
    tcB->name = @"A";
}

有什么办法可以直观看到实例对象tcAtcB的内存结构吗?

0x02 直观了解实例对象内存结构

通过NSData+ (instancetype)dataWithBytes:(nullable const void *)bytes length:(NSUInteger)length;方法,可以我们直观看到对象的内存结构!

那么,修改main函数如下:

int main(int argc, char * argv[]) {

  //创建实例tcA
  TestClass *tcA = [[TestClass alloc] init];
  tcA->myInt = 100;
  tcA.age = 7;
  tcA->name = @"libai";

  //创建实例tcB
  TestClass *tcB = [[TestClass alloc] init];;
  tcB->myInt = 101;
  tcB.age = 5;
  tcB->name = @"gongben";

  //新增代码,将对象转换为字节流Data
  long tcSize = class_getInstanceSize([TestClass class]);
  NSData *tcAData = [NSData dataWithBytes:(__bridge const void *)tcA length:tcSize];
  NSData *tcBData = [NSData dataWithBytes:(__bridge const void *)tcB length:tcSize];


  NSLog(@"TestClass object tcA contans %@", tcAData);
  NSLog(@"TestClass object tcB contans %@", tcBData);
  NSLog(@"tcSize size= %zu", tcSize);
  NSLog(@"TestCalss memory address = %p", [TestClass class]);

}

输出日志如下:

2018-05-10 14:54:27.068685+0800 TestApp[79148:1558980] TestClass object tcA contans <58f25f05 01000000 64000000 00000000 f8e15f05 01000000 07000000 00000000>
2018-05-10 14:54:27.069407+0800 TestApp[79148:1558980] TestClass object tcB contans <58f25f05 01000000 65000000 00000000 18e25f05 01000000 05000000 00000000>
2018-05-10 14:54:27.069653+0800 TestApp[79148:1558980] tcSize size= 32
2018-05-10 14:54:27.069795+0800 TestApp[79148:1558980] TestCalss memory address = 0x1055ff258

0x03 字节流分析

那么下面这两字节流分别怎么理解?

 TestClass object tcA contans <58f25f05 01000000 64000000 00000000 f8e15f05 01000000 07000000 00000000>
 TestClass object tcB contans <58f25f05 01000000 65000000 00000000 18e25f05 01000000 05000000 00000000>

首先我们知道TestClass 继承于NSObject, 而NSObject的结构如下,它只有一个成员或者叫实例变量

@interface NSObject <NSObject> {
  Class isa  OBJC_ISA_AVAILABILITY;
}

那么结合TestClass被本身也具备的个实例变量:myIntnameage ,那么我们可以猜测TestClass的实例对象的内存结构如下:

  1. 第一个8字节58f25f05 01000000

    根据Little Endian 序(注2)规则”低地址存放低位,高地址存放高位“,那么该字节还原成十六进制值是00000001055ff2580x1055ff258。与上述第4条日志打印的TestCalss的地址相等。

  2. 第二个8字节64000000 00000000

    同上,那么该字节还原成十六进制值是000000 0000000064,十进制为100。与tcA->myInt的值相等

  3. 第三个8字节f8e15f05 01000000

    同上,那么该字节还原成十六进制值是000000010055fe1f8,在gbd调试环境下,通过输出该地址的值,与tcA->name的值相等

    (lldb) po (NSString *)0x0000000100537218
    libai
    (lldb)
  4. 第四个8字节07000000 00000000

    同上,那么该字节还原成十六进制值是000000 0000000007,十进制为7。与tcA.age的值相等

由此可见,实例对象的内存结构正如上图那样分布!

0x04 实例对象大小

从第三条日志得到,tcA或者tcB的内存大小都是32字节

2018-05-10 14:54:27.069653+0800 TestApp[79148:1558980] tcSize size= 32

通过计算各个成员变量的值,得到的结果是28字节

sizeof(tcA->myInt) + sizeof(tcA->name) + sizeof(tcA.age) + sizeof(tcA->isa) = 4 + 8 + 8 + 8 = 28

也就是说,sizeof(对象)的大小,并不一定等于各个数据成员的大小之和!这涉及到”内存字节对齐

参考:内存字节对齐

struct x_ {
    char a;     // 1 byte
    int b;      // 4 bytes
    short c;    // 2 bytes
    char d;     // 1 byte
} MyStruct1;

struct y_ {
    int b;      // 4 bytes
    char a;     // 1 byte
    char d;     // 1 byte
    short c;    // 2 bytes
} MyStruct2;

NSLog(@"%lu,%lu", sizeof(MyStruct1), sizeof(MyStruct2));

上述代码打印出来的结果为:12,8


  1. 如何确定指针大小

    int numb = 12;
    int *p = &numb;
    NSLog(@"指针p大小:%zu bytes", sizeof(p));//指针p大小:8 bytes
    
  2. 如何确定字节序

    参考:字节序:Big Endian 和 Little Endian

    什么是大端与小端(Little Endian 与 Big Endian)

    • Big Endian 是指低地址端 存放 高位字节。
    • Little Endian 是指低地址端 存放 低位字节。

    那么,直接使用代码通过输出直接来验证是大端还是小端

    int a = 0x12345678;
    if( *((char*)&a) == 0x12)
      printf("Big Endian");
    else
      printf("Little Endian");

    输出

    Little Endian
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值