一、NSArray
NSArray创建的是不可变对象的数组,即一旦创建了一个包含特定数量的对象的数组,就不能对数组进行添加、删除元素的操作。
为了方便观察NSArray的内存分布,在NSArray对象中存储同一个字符串:
NSString *str = @"11";
NSLog(@"字符串常量的地址 %p", str);
NSArray *array = @[str, str, str, str, str];
// 字符串常量的地址 0x108cc2360
str
是字符串常量,所以指向的地址是固定的。然后利用x/100xb array
指令观察array内存分布
- 从图中很明显发现,第三行到第七行所存储地址正好就是字符串常量的地址。
- 根据OC对象的定义,前8个字节(对应第一行内容),是
isa
(包含对象信息、引用计数、类指针...)
- 经过多次调试,可以发现第二行地址存储的为数组的长度。
所以很明显,NSArray创建时,会根据所存储内容申请一片连续的空间,也由于这种内存分配方式,不方便进行内存调整,也符合了不可变数组的定义。
一、NSMutableArray
NSMutableArray 继承自 NSArray,是一个可变数组,可以对数组的元素进行增删改查。
通NSArray一样,在NSMutableArray对象中存储同一个字符串:
NSString *str = @"11";
NSLog(@"字符串常量的地址 %p", str);
NSMutableArray *array = [NSMutableArray arrayWithCapacity:1];
for (int i = 0; i < 10; i ++) {
[array addObject:@"11"];
}
// 字符串常量的地址 0x10043e360
- 和NSArray类似,第一行存储的依然
isa
- 内存中并没有找到字符串常量的地址,经猜测和调试验证,数组中的内容单独存储在另外一块连续空间中,再将数组元素首地址存在在数组中;
根据内存分布情况,可以通过修改指针直接获取内存中的值,通过下面代码可分别获取数组内容首地址的值和数组容量capacity
的值
@implementation NSArray (address)
- (void *)elementsAddress {
void *address = (__bridge void *)self;
return *((void **)address + 2);
}
- (int)capacity {
void *address = (__bridge void *)self;
return *((int *)address + 7);
}
@end
通过多次调试,可发现,数组扩容的规律,也并不是网上所说的容量不够时,会申请一块双倍容量的空间。而且通过
arrayWithCapacity
方法创建时,会提前申请一块连续的空间,节省了很多扩容的操作,但是并不一定和传递进去的容量一致(例如参数为100时,实际容量只有16)。所以数组通过这种间接指向的方式,可以方便的进行扩容,以及数据的修改。
数组扩容流程:
- 申请一块新的空间
- 将原数据拷贝到新的地址中去
- 释放原数据存储空间,并将指针指向新的内存区域
文中用到的
x
指令,是memory read
的缩写,其中b
代表以字节为单位进行打印,x
代表以16位格式显示数据。x/100xb array
就代表打印array地址开始的100个字节的内容以16位格式显示