iOS NSArray和NSMutableArray原理

1. NSArray

iOS在创建NSArray时,一般创建的是__NSArrayI对象,但当其只包含一个元素时,创建的是__NSSingleObjectArrayI对象,没有任何对象时,创建的是__NSArray0对象,__NSArrayI__NSSingleObjectArrayI__NSArray0都继承自NSArray。

1.1 __NSSingleObjectArrayI

__NSSingleObjectArrayI的结构定义为:

@interface __NSSingleObjectArrayI : NSArray
{
    id object;
}
@end

__NSSingleObjectArrayI类内部结构简单,只增加了一个存储对象的属性。

1.2 __NSArrayI

__NSArrayI的结构定义为:

@interface __NSArrayI : NSArray
{
    NSUInteger _used;
    id _list[0];
}
@end

_used是数组元素个数,调用[array count]返回的就是_used的值。
id _list[0]是c语言里面的柔性数组,不占用内存,且相对指针效率更高。

2. NSMutableArray

iOS 创建NSMutableArray时,实际得到的是其子类__NSArrayM对象。__NSArrayM的结构定义为:

@interface __NSArrayM : NSMutableArray
{
    NSUInteger _used;
    NSUInteger _offset;
    int _size:28;
    int _unused:4;
    uint32_t _mutations;
    id *_list;
}
@end

_list是存储对象数组的一块连续的内存区域
_used是数组元素个数
_offset起始偏移量
_size能存储的的对象个数,_list大小。(大于或等于_used)
_mutations修改标记,每次对__NSArrayM的修改操作都会使_mutations加1,“*** Collection <__NSArrayM: 0x1002076b0> was mutated while being enumerated.”这个异常就是通过对_mutations的识别来引发的。

分析:__NSArrayM并没有使用链表结果存储内存,链表虽然在增删方面有不需要移动数据的有优势,但是查找,分配,占用空间方面效率低下。在查找方面,数组可通过内存地址偏移迅速找到需要的对象,另一方面,因为内存连续,而且每个数组元素都只是一个指针,所以拷贝起来也很快。
__NSArrayM内部是采用数组方式实现环形缓冲区,实现环形缓冲区相对简单数组方式,在增删方面作了一定优化,尽量避免数据移动或减少移动次数。
扩容:当__NSArrayM存储容量不足时,会重新malloc一块新的存储区域,并将数据复制到新的区域,最后释放旧的存储空间。
arrayWithCapacity:方法可以指定__NSArrayM的初始容量,也就是_size/环形缓冲区的初始大小。

3. 遍历性能

对不同数组遍历方式进行性能测试,结果如图所示:


不同数组遍历方式性能对比

结果显示:
for in> for > 普通枚举 > 多线程并发枚举(NSEnumerationConcurrent),for in的方式性能最佳。
多线程并发枚举(NSEnumerationConcurrent)的回调是乱序,但因为线程开销相对执行任务开销(测试遍历速度,任务开销接近0)巨大,性能测试结果最差。如果在执行任务开销巨大的场景中,该方式因为采取多线程,可能更适合。
for in性能最好,是因为NSArray的几个子类都实现了NSFastEnumeration快速枚举协议。它直接从C数组中取对象。对于可变数组来说,它最多只需要两次就可以获取全部元素。如果数组还没有构成循环,那么第一次就获得了全部元素。但是如果数组构成了循环,那么就需要两次,第一次获取对象数组的起始偏移到循环数组末端的元素,第二次获取存放在循环数组起始处的剩余元素。而for循环之所以慢一点,是因为for循环的时候每次都要调用objectAtIndex:

参考:https://www.jianshu.com/p/66f8410c6bbc

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 有关NSArray的 不管是NSArray,还是NSMutableArray ,alloc之后的得到都是__NSP...
    tanghaiyang阅读 8,199评论 0 8
  • 在iOS开发中,我们在非常非常多的地方用到了数组。而关于数组,有很多需要注意和优化的细节,需要我们潜入到下面,去了...
    伯陽阅读 11,248评论 3 30
  • OC数组的类体系 当我们创建一个NSArray对象时,实际上得到的是NSArray的子类__NSArrayI对象。...
    _zhw阅读 3,744评论 0 1
  • 数组的遍历,这个话题貌似没什么好探究的,该怎么遍历就怎么遍历呗!但是如果要回答这些问题:OC数组有哪几种遍历方式?...
    RenJK阅读 14,174评论 12 84
  • 原文地址 本文翻译自 Exposing NSMutableArray,译文原地址为NSMutableArray 原...
    oneday527阅读 9,509评论 10 21

友情链接更多精彩内容