美文网首页
NS(Mutable)Array的原理

NS(Mutable)Array的原理

作者: _zhw | 来源:发表于2020-04-16 14:32 被阅读0次
    OC数组的类体系

    当我们创建一个NSArray对象时,实际上得到的是NSArray的子类__NSArrayI对象。同样的,当我们创建NSMutableArray对象时,实际上得到的是其子类__NSArrayM对象。不过,当我们创建空的或只有一个元素的NSArray对象时,得到的分别是__NSArray0__NSSingleObjectArrayI对象。

    OC数组的类体系
    为什么使用NSArrayNSMutableArray,却可以得到其子类?

    这个机制可以总结为以下两大步骤
    1.NSArray重写了+ (id)allocWithZone:(struct _NSZone *)zone方法。在方法内部,如果调用类为NSArray则直接返回全局变量___immutablePlaceholderArray;如果调用类为NSMutableArray则直接返回全局变量___mutablePlaceholderArray
    也就是调用[NSArray alloc]或者[NSMutableArray alloc]得到的仅仅是两个占位指针,类型为__NSPlaceholderArray
    2.在调用了alloc的基础上,不论是NSArrayNSMutableArray都必定要继续调用某个initXXX方法,而实际上调用的是__NSPlaceholderArrayinitXXX。在这个initXXX方法内部,如果self == ___immutablePlaceholderArray就会重新构造并返回__NSArrayI 对象,如果self == ___mutablePlaceholderArray就会重新构造并返回_NSArrayM对象。

    总结来说,对于NSArrayNSMutableArrayalloc时拿到的仅仅是个占位对象,init后才得到真实的子类对象。

    上面提到了一个私有类__NSPlaceholderArray,其内部包含有两个全局变量___immutablePlaceholderArray___mutablePlaceholderArray

    OC数组的内部结构

    NSArrayNSMutableArray,只是定义和实现了接口,而且对内部数据操作的接口都是在各个子类中实现的。所以真正需要了解的是其子类结构,了解__NSArrayI就相当于了解NSArray,了解__NSArrayM就相当于了解NSMutableArray

    1.__NSArrayI

    __NSArrayI的结构定义为:

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

    _used是数组的元素个数
    _list[0]是OC数组内部实际存储对象的数组

    2.__NSSingleObjectArrayI

    __NSSingleObjectArrayI的结构定义为:

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

    因为只有一个元素,所以只需一个object即可。

    3.__NSArrayM

    __NSArrayM的结构定义为:

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

    _used是数组的元素个数
    _offset实际存储对象的数组的起始偏移
    _size已分配的_list大小
    _mutations修改标记,每次对__NSArrayM的修改操作都会使_mutations加1
    _list是个环形缓冲区,必要时会动态重新分配内存空间以符合当前存储需求

    以一个初始包含5个元素,总大小_size为6的_list为例:
    _offset = 0, _used = 5, _size = 6


    在末端追加3个对象后:
    _offset = 0, _used = 8, _size = 8
    _list已重新分配

    删除对象A:
    _offset = 1, _used = 7, _size = 8

    删除对象E:
    _offset = 2, _used = 6, _size = 8
    B、C往后移动了,E的空缺被填补(移动最少元素的一边)

    在末端追加两个对象:
    _offset = 2, _used = 8, _size = 8
    _list足够存储新加入的两个对象,因此没有重新分配,而是将两个新对象存储到了_list起始端

    因此可见,__NSArrayM_list是个环形缓冲区,它的起始由_offset标识。

    参考链接:
    https://www.jianshu.com/p/66f8410c6bbc
    https://www.sohu.com/a/235998120_208051

    相关文章

      网友评论

          本文标题:NS(Mutable)Array的原理

          本文链接:https://www.haomeiwen.com/subject/wwfrvhtx.html