参考链接:
NSMutableArray分析
他的github主页

古老的C数组:

优点:查询速度很快,直接通过下表找到对应的值
缺点:修改、删除数据很慢,需要移动基于所有的其他的元素


插入和删除一个元素,需要移动后面所有的元素

改进的目标:实现快速增加,删除

NSMutableArray的内部数据结构如下:


几个主要变量:
offset: 有效数据起始位置偏移量
size: 实际占用的内存大小
used: 数组的实际的有效数据个数
*list: 实际内存的起始地址

删除元素

1
2
[arr removeObjecAtIndex:0];
[arr removeObjecAtIndex:0];

执行两次 remove的实现:

仅仅修改 offset即可,内存完全不需要移动。
那么问题来了,移走的这个元素的内存怎么释放的呢?

回到OBJC的引用计数,offset移走,这个对象的引用计数减一,对象引用计数到0之后,由autoreleasepool释放。over

插入元素

1
[arr insertObjec:@"test" atIndex:0];

执行insertObjec的实现:

如果buff的size还够用,不需要扩展buff,数据会在buff的末端添加进去,此时offset由0变成size-1,used+1.over
循环buff的牛逼之处就在于此,无需移动内存,实现插入元素。

删除元素

1
[arr removeObjecAtIndex:3];

实现:

删除头尾元素直接修改offset或者used即可
但是如果删除中间元素,就避免不了移动其他元素,不过NSArray会选择更少移动的那一边移动数据。
所以我们在实际使用过程中应该尽量避免这么做。

tip:

1
2
3
4
5
6
NSMutableArray *array = [NSMutableArray array];

for (int i = 0; i < 10000; i++) {
[array addObject:[NSObject new]];
}
[array removeAllObjects];

数组移除所有元素,buff并不会被立即清空
输出size的结果:

1
Size: 14336

如果你在利用NSArray保存大量数据的时候,就要注意了。remove之后,记得置为nil,才能立刻释放掉buff

总结:

1.数组越界奔溃: index > _used+offset 或 index < 0
2.如果想要内存记录释放,remove之后记得置nil.或者直接置nil.猜想Array的dealloc的方法会自动给所有元素发release消息