在上一篇的流程图中,我们看到最后的流程中,在_class_createInstanceFromZone,我们分为三步:
这一篇我们来分析获取需要分配的大小,以及具体如何分配内存,也就是我们的第1步和第2步。
size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
我们从alignedInstanceSize开始看,这个方法中
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
这个方法中的unalignedInstanceSize我们再来看看
uint32_t unalignedInstanceSize() const {
ASSERT(isRealized());
return data()->ro()->instanceSize;
}
我们来分析data()->ro()->instanceSize,data()实现为:
class_rw_t *data() const {
return bits.data();
}
class_ro_t是类在编译器存储类信息的数据结构,它里面包含了类的实例变量、方法列表、协议列表等,class_rw_t是用来存储 在dyld的_map_images方法中将分类的各种信息与class_ro_t合并后的信息。
data()->ro()->instanceSize也就是获取对象所有实例变量需要的存储大小。
我们再看word_align的实现
# define WORD_MASK 7UL
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
这个方法先将大小加上7,然后和~WORD_MASK进行逻辑与,由于7的二进制为0000 1111,对它取反后1111 0000,再与前面进行与,那么我们的内存分配的大小就是8的倍数了,也就是8字节对齐
最后,if (size < 16) size = 16;这一步就是如果不足16字节,那么我们就补足16,所以一个OC对象,至少是16个字节。
整个步骤流程如下:
我们在OC中获取内存大小的三种方式分别是:
我们创建一个LWTestClass来测试
@interface LWTestClass : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) long height;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LWTestClass *person = [LWTestClass alloc];
person.name = @"Cooci";
person.nickName = @"KC";
NSLog(@"%@ - %lu - %lu - %lu",person,sizeof([LWTestClass class]),class_getInstanceSize([LWTestClass class]),malloc_size((__bridge const void *)(person)));
}
return 0;
}
打印结果: - 8 - 40 - 48
如上面的结果
我们在开篇中所说的第一步是获取对象所需空间大小,这里我们只能确定对象内存的内存布局是8字节对齐,这也是class_getInstanceSize获取到的内存大小,malloc_size获取到的是系统分配的内存大小,这是属于我们第二步中obj = (id)calloc(1, size);的内容,calloc方法的分析我们需要用libmalloc源码,具体的调用顺序我们不一一列举,大致的调用过程如以下流程图与核心代码。
#define SHIFT_NANO_QUANTUM 4
#define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16
static MALLOC_INLINE size_t
segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey)
{
size_t k, slot_bytes;
//size为0,补为16
if (0 == size) {
size = NANO_REGIME_QUANTA_SIZE; // Historical behavior
}
//size加上15再右移4位,将低4位给丢弃
k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta
//再左移4位,这样得到的大小就是16的倍数
//内存分配也就是以16字节对齐了
slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size
*pKey = k - 1; // Zero-based!
return slot_bytes;
}
所以,在前面的例子中,我们计算出对象所需的内存字节大小为40,进行16字节对齐后,我们实际分配的内存大小为48.
这个没找到具体的答案,猜想如下:
这段有想法的大佬希望可以留言给予指导
上面我们说内存对齐,那么内存对齐有什么规则呢?为什么要内存对齐呢?
struct/class/union内存对齐原则有四个:
我们设置3个结构体,并分别打印它们所占字节大小
struct TestStruct1{
double a;
int b;
bool c;
short d;
}st1;
struct TestStruct2{
int a;
double b;
bool c;
short d;
}st2;
struct TestStruct3{
int a;
double b;
struct TestStruct1 st;
bool c;
short d;
}st3;
int main(int argc, const char * argv[]) {
NSLog(@"st1:%lu,st2:%lu,st3:%lu",sizeof(st1),sizeof(st2),sizeof(st3));
//打印结果:st1:16,st2:24,st3:40
return 0;
}
从结果可以看到,st1和st2结构体中所含有的数据类型与个数相同,但位置不同,它们所占的大小也变得不一致了。
下面我们来分析它们分别的内存构成:
三个结构体的内部布局如下图
回答这个问题,我们要从内存、CPU以及平台三个方向来回答
移植原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
我们先讲讲内存结构
作者:默默_David
链接:https://www.jianshu.com/p/d285a33bf88f
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
页面更新:2024-04-25
本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828
© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号