Swift源码分析-Array
Swift源码分析 - Array
ContiguousArray:A fast, contiguously-stored array of Element.(快速连续存储的数组)
ArraySlice:The Array
-like type that represents a sub-sequence of any Array
, ContiguousArray
, or other ArraySlice
.(可以是ContiguousArray,Array,ArraySlice的子序列)
Array:is an efficient, tail-growable random-access collection of arbitrary elements.(一个高效的,可增长的,可随机访问的集合)
除了Array
能和Objective-c
桥接,其他都不行.
Array
1 | /// Construct a Array of `count` elements, each initialized to |
数组初始化为同一个值:先分配一块没有初始化的区域,再逐个初始化.
1 | /// The number of elements the Array stores. |
第一个是数组的个数,第二个至关重要!!因为这关系到数组的性能,在后面补充会详细说.
1 | /// Extend `lhs` with the elements of `rhs`. |
从这我们可以看出,数组也是可以用`+=`运算符的,前提数组中存储的元素是同一个类型。
1 |
|
这个是比较数组是否相同的代码,仔细看lhs._buffer.identity == rhs._buffer.identity
可以猜出,每个数组都有一个唯一id,通过判断id来判断数组是否是同一个’引用’
#补充:
数组中capacity和count的区别
capacity:容量
和count:数量
,至于为什么要弄这两个参数,那就要提到数组实现原理了.
在分析之前先看结果:
数组的实现原理
我们在使用数组的时候通常都是这样的:
1 | var array: [Int] = [] |
我们从最底层的C语言开始比较:
1 | //C语言中我们这样写 |
当我们执行int array[5]
的时候就已经分配好了内存空间,不管你数组中只有一个数,所占空间都是5*sizeof(int)
.
但是在swift中好像我们也没有分配具体大小的空间,感觉要多少有多少,从来没有关心容量不够的情况.
那是因为Swift已经为我们弄好了,但是这也是一个坑!!!下面是append
方法实现.
1 | /// Append `newElement` to the Array. |
我们再调用append方法的时候实现了数组的扩容,这样保证不浪费内存空间,但是我们知道在C语言中我们malloc
一块内存用来存放数据,当不够的时候我们会采用realloc
.而realloc
的原理大家都是知道了,就是重新找一块内存分配空间,然后将原来的数据复制过去.
Swift的做法和这个差不多,我们来看看:
仔细的看内存地址和之前的那张对比,是有规律的,2,4,8,16…每次分配的大小为2的N次方.这在数组很小的时候是有用的,但是如果我们存储33个数,其实Swift为我们分配了64的存储空间,这样就了白白浪费了很多的空间,而且随着数组数量的增大,越明显,但是Apple其实也知道,在平常的开发中很难遇到那么多的数.
Note: 这样一来就应该明白count和capacity的区别了,count表示数组中实际存在的元素个数,而capacity表示数组的容量大小.剩下的就是空闲的,避免每次
append
都要重新拷贝数据.
下面的这个函数我们不经常用到,但是如果你不想白白浪费空间,你可以使用.前提你要知道你的数组大概是多少个,下面这个方法就是设定一个最小分配容量.
1 | /// Reserve enough space to store `minimumCapacity` elements. |
我们可以看到Swift为我们分配了10个空间,并且增长方式也变成了10*2的N次方.
Note:由此我们其实可以得出一个重要的概念
append方法不是线程安全的
,因为每次使用append的时候内存地址都有可能在改变,当多个线程同时往数组中添加或者读取数据的时候,会存在一个线程已经改变数组的地址,但是另一个线程却还在原来的地址读写数据,导致发生错误.
反正就是各种错误,那么如何正确的在多线程的环境下使用数组呢?我们用reserveCapacity
方法来最小分配一个空间,这样内存地址就不会随意的改变了.
Note:这样只能保证在添加数据的时候不会出现访问野指针的情况.图示显示的
length
一个是1021,一个是1999,说明任务是并发执行的.