01.alloc与字节对齐
一、探索alloc
看下面的例子,输出会是什么样的呢?
Ryukie *obj1 = [Ryukie alloc];
Ryukie *obj2 = [obj1 init];
Ryukie *obj3 = [obj1 init];
NSLog(@"Objc1:%p,他的指针:%p", obj1, &obj1);
NSLog(@"Objc2:%p,他的指针:%p", obj2, &obj2);
NSLog(@"Objc3:%p,他的指针:%p", obj3, &obj3);输出如下:
2021-06-06 19:56:22.045515+0800 OCObject[10944:3914848] Objc1:0x28220bc60,他的指针:0x16d421bf8
2021-06-06 19:56:22.045611+0800 OCObject[10944:3914848] Objc2:0x28220bc60,他的指针:0x16d421bf0
2021-06-06 19:56:22.045659+0800 OCObject[10944:3914848] Objc3:0x28220bc60,他的指针:0x16d421be8不同的指针,指向了同样的内存空间
二、通过源码了解alloc实现
NSObject.mm这里就是NSobject的核心实现代码
2.1 可运行调试的源码
将项目Clone下来,即可运行调试。更加直观的学习底层实现。
2.2 调用流程探究
a. 实验代码
我们在main中写下如下代码,分别在<-处添加断点
b. alloc
现在是RYModel第一次调用alloc。字面实现如下:
按照代码下一步应该会调用_objc_rootAlloc,真的吗?
c. objc_alloc
我们发现断点走到了这里,正如注释里面所说:Calls [cls alloc].
我们推断,这里应该是alloc的实现IMP被修改了。继续下一步
d. callAlloc
断点直接来到了 objc_msgSend 处,又继续调用了 alloc
e. _objc_rootAlloc
这次断点才来到了,_objc_rootAlloc
f. _objc_rootAllocWithZone
又来到了callAlloc方法中,这次断点来到了_objc_rootAllocWithZone
_objc_rootAllocWithZone的实现:
我们继续下一步
g. _class_createInstanceFromZone 创建实例
g.1 instanceSize 大小
这里会去缓存中查找
CoreFoundation规定最小16个字节有
isa指针的8字节还有8字节预留
// Class's ivar size rounded up to a pointer-size boundary.成员变量的大小向上取整(一个指针的大小8字节)
即:成员变量的字节对齐
即:不足8字节就按8字节
// May be unaligned depending on class's ivars.大小取决于成员变量的大小
g.2 calloc 开空间
这里分配的还空间内还有数据,因为内存只覆写,不删除。
g.3 initInstanceIsa 初始化实例isa
这里会将刚创建的obj和isa进行绑定,完成这一步后,可以发现,obj就会先变成RYModel了。
h. 非第一次alloc
发现在d. callAlloc这一步中有所不同:
没有走
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));而是直接走了
_objc_rootAllocWithZone
这里是LLVM做了处理,对alloc的IMP做了调整,在类第一次调用alloc时进行了一些处理,然后再调用原有的alloc实现。
i. 流程图
图片较大较长,建议下载到本地

三、字节对齐
3.1 算法
3.2 先思考一个问题:

显然第二种效率更高,但是牺牲了一定的空间。
这就是典型的
以空间,换时间,对于CPU来说处理速度非常重要
3.3 内存调试
我们在第一个demo中下个断点

(long) $1 = 80361110149439809这里其实是isa指针,但是打印出来确实long类型?
3.4 isa 与 MASK
a.获取isa指针
这里要拿到isa需要&一个mask,在isa.h中可以找到对应架构的定义
b.获取MASk
下面是
ARM64的,其他架构的可以自行在头文件中查找,很简单的
c.复原出isa
再次输出
成功输出了Class: Ryukie
3.5 查看真实的内存排列方式
a.一个内存对齐的场景
首先我们格式化打印分析 x/4gx
这种方式更加直观,不用我们自己去按小端的方式反着看了。
4代表打印4个单位的,可以按需要调整。

这里发现
po 0x0000001200000001结果很怪异77309411329这里我们还剩
age&isboy两个成员变量没有输出将这里的16位分开看下
po 0x0000001218
po 0x000000011
这里Bool只占一个字节但是拉伸为了8个字节
这里就是比较明显的内存对齐的一个场景
b.思考
如果我们将上面的
@property (nonatomic, assign) int age;改为@property (nonatomic, assign) NSInteger age;会是怎样呢?
Last updated
Was this helpful?