美文网首页
CFArray源码解读

CFArray源码解读

作者: 黄二瓜 | 来源:发表于2020-11-19 18:21 被阅读0次

    1.关键代码

    关键代码如下,其中具体内容见代码注释部分。在注释文档中,以#数字开始的表示关键节点序号,后续实际分析时会使用到。

    1.1 CFArrayRef 相关数据结构

    // 保存数组元素的指针
    struct __CFArrayBucket
    {
        const void *_item;
    };
    
    // 可变数组时使用的双端序列结构体
    struct __CFArrayDeque 
    {
        uintptr_t _leftIdx; //元素在deque中起始下标
        uintptr_t _capacity; //数组容量,通常情况下是实际元素个数的2倍,最小为4
        /* struct __CFArrayBucket buckets follow here */ // 元素容器
    };
    
    // CFArrayRef 结构体
    struct __CFArray
    {
        CFRuntimeBase _base;
        CFIndex _count; /* number of objects */ //实际元素个数
        CFIndex _mutations; // 变化次数
        int32_t _mutInProgress;
        __strong void *_store; /* can be NULL when MutableDeque */ 
    };
    

    数据结构大概是这样的:

    CFArray数据结构

    1.2数组初始化数方法

    // #1.初始化数组具体实现
    static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFArrayCallBacks *callBacks) {
        ...
        switch (__CFBitfieldGetValue(flags, 1, 0))
        {
        case __kCFArrayImmutable: //不可变数组
            if (isWeakMemory(memory))
            { // if weak, don't scan
                auto_zone_set_unscanned(objc_collectableZone(), memory);
            }
            if (__CFOASafe)
                __CFSetLastAllocationEventName(memory, "CFArray (immutable)");
            break;
        case __kCFArrayDeque: //可变数组
            if (__CFOASafe)
                __CFSetLastAllocationEventName(memory, "CFArray (mutable-variable)");
            ((struct __CFArray *)memory)->_mutations = 1;
            ((struct __CFArray *)memory)->_mutInProgress = 0;
            //#1.1初始化可变数组时,store为NULL
            ((struct __CFArray *)memory)->_store = NULL; 
            break;
        }
        ...
    }
    

    1.3更新数组元素方法

    // This function does no ObjC dispatch or argument checking;
    // It should only be called from places where that dispatch and check has already been done, or NSCFArray
    // #2 变动内部元素
    void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void **newValues, CFIndex newCount)
    {
        CHECK_FOR_MUTATION(array);
        BEGIN_MUTATION(array); // 加锁
    
        // #2.1 声明并初始化变量: idx, cnt: 当前元素数量, futureCnt: 更新后元素数量
        const CFArrayCallBacks *cb;
        CFIndex idx, cnt, futureCnt;
        const void **newv, *buffer[256];            //声明 newv 数组、buffer 缓冲区
        cnt = __CFArrayGetCount(array);             //当前元素数量
        futureCnt = cnt - range.length + newCount;  //数组更新后元素数量
        CFAssert1(newCount <= futureCnt, __kCFLogAssertion, "%s(): internal error 1", __PRETTY_FUNCTION__);
        cb = __CFArrayGetCallBacks(array);                  //获取回调,内部通过标记位的值来判断
        CFAllocatorRef allocator = __CFGetAllocator(array); //获取分配器?
    
        /* Retain new values if needed, possibly allocating a temporary buffer for them */
        /* 如果有必要,retain有新的元素,并为其分配一个临时缓冲区 */
        if (NULL != cb->retain && !hasBeenFinalized(array)) //如果retain存在
        {
            // newCount元素数量小于等于256,则使用256个长度的缓冲区, 否则使用newCount个长度的缓冲区
            newv = (newCount <= 256) ? (const void **)buffer : (const void **)CFAllocatorAllocate(kCFAllocatorSystemDefault, newCount * sizeof(void *), 0); // GC OK
            if (newv != buffer && __CFOASafe)
                __CFSetLastAllocationEventName(newv, "CFArray (temp)");
            for (idx = 0; idx < newCount; idx++)
            {
                newv[idx] = (void *)INVOKE_CALLBACK2(cb->retain, allocator, (void *)newValues[idx]); //新元素数组赋值
            }
        }
        else
        {
            newv = newValues;
        }
        array->_mutations++; //抖动量+1
    
        /* Now, there are three regions of interest, each of which may be empty:
         *   A: the region from index 0 to one less than the range.location
         *   B: the region of the range
         *   C: the region from range.location + range.length to the end
         * Note that index 0 is not necessarily at the lowest-address edge
         * of the available storage. The values in region B need to get
         * released, and the values in regions A and C (depending) need
         * to get shifted if the number of new values is different from
         * the length of the range being replaced.
         */
        // 将数组的分为三部分
        // A: 从下标0到range.location
        // B: range部分,即从 range.location 到 range.location + range.length 
        // C: 下标从 range.location + range.length 到末尾
        // |        A       |       B       |       C       |
        // 0                range.location  range.length    end
        if (0 < range.length)
        {
            __CFArrayReleaseValues(array, range, false); //释放数组中被替换的对象,即B部分
        }
        // region B elements are now "dead"
        if (0)
        {
        }
        // #2.2 如果 arrary->_store 为NULL, 则初始化store、deque(第一次对元素做操作时会进来)
        else if (NULL == array->_store) 
        {
            if (0)
            {
            }
            else if (0 <= futureCnt)
            {
                struct __CFArrayDeque *deque; //如果store为空,声明一个双端队列
                CFIndex capacity = __CFArrayDequeRoundUpCapacity(futureCnt); //确定容量大小, min(min(futureCnt * 2, long_max), 4), 最小为4
                CFIndex size = sizeof(struct __CFArrayDeque) + capacity * sizeof(struct __CFArrayBucket); //空间大小: 
                deque = (struct __CFArrayDeque *)CFAllocatorAllocate((allocator), size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0); //分配空间
                if (__CFOASafe)
                    __CFSetLastAllocationEventName(deque, "CFArray (store-deque)");
                deque->_leftIdx = (capacity - newCount) / 2; //确定双端队列左值
                deque->_capacity = capacity;
                __CFAssignWithWriteBarrier((void **)&array->_store, (void *)deque);
                // 释放
                if (CF_IS_COLLECTABLE_ALLOCATOR(allocator))
                    auto_zone_release(objc_collectableZone(), deque); // GC: now safe to unroot the array body.
            }
        }
        else 
        { // Deque
            // reposition regions A and C for new region B elements in gap
            // #2.3 根据B区域元素,重新定位A、C区域
            if (0)
            {
            }
            else if (range.length != newCount)
            {
                __CFArrayRepositionDequeRegions(array, range, newCount);
            }
        }
        // copy in new region B elements
        // #2.4 将变动数据拷贝到B区域
        if (0 < newCount)
        {
            if (0)
            {
            }
            else
            { // Deque
                struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store;
                // raw_buckets指向deque中bucket队列头
                struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque));
                // 移动B  objc_memmove_collectable(obj, oldObj, size): 把旧对象的内存复制到新对象
                objc_memmove_collectable(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket));
            }
        }
        // #2.5 设置新的元素个数
        __CFArraySetCount(array, futureCnt);
        // 释放newv缓冲区
        if (newv != buffer && newv != newValues)
            CFAllocatorDeallocate(kCFAllocatorSystemDefault, newv);
        // 解锁?
        END_MUTATION(array);
    }
    

    在对数组进行操作时,会将数组分成A、B、C 三部分;

    • A: 从下标0到range.location
    • B: range部分,下标从 range.location 到 range.location + range.length
    • C: 下标从 range.location + range.length 到末尾

    /* Now, there are three regions of interest, each of which may be empty:
    * A: the region from index 0 to one less than the range.location
    * B: the region of the range
    * C: the region from range.location + range.length to the end
    * Note that index 0 is not necessarily at the lowest-address edge
    * of the available storage. The values in region B need to get
    * released, and the values in regions A and C (depending) need
    * to get shifted if the number of new values is different from
    * the length of the range being replaced.
    */

    以向数组中"a"和"c"之间插入"c"为例子:

    A、B、C 三部分划分.png

    2.使用示例及流程分析

    执行以下代码,并进行分析:

     // 创建可变长度数组
    CFMutableArrayRef mutableArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
     // 插入"a"
    CFArrayInsertValueAtIndex(mutableArray, 0, CFSTR("a"));
    // 插入"b"
    CFArrayInsertValueAtIndex(mutableArray, 1, CFSTR("b"));
    

    2.1创建可变数组

    CFMutableArrayRef mutableArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
    

    内部调用顺序CFArrayCreateMutable --> __CFArrayCreateMutable0 --> __CFArrayInit

    #1.1: 初始化可变数组时, _storeNULL

    初始化

    2.2 向可变数组中插入数据"a"

    CFArrayInsertValueAtIndex(mutableArray, 0, CFSTR("a"));
    

    当第一次插入元素“a”时:

    #2.1:

    range = (0, 0);
    newCount = 1;
    cnt = 0;
    futurecnt = 1;
    

    #2.2: 此时 array->_storeNULL, 会初始化_storedeque

    capacity = 4;
    deque->_leftIdx = 1;
    deque->capacity = 4;
    ...
    deque = (struct __CFArrayDeque *)CFAllocatorAllocate((allocator), size, isStrongMemory(array) ? __kCFAllocatorGCScannedMemory : 0);
    __CFAssignWithWriteBarrier((void **)&array->_store, (void *)deque);
    
    初始化_store、deque

    #2.4: 移动B部分

    使用objc_memmove_collectable(obj, oldObj, size) (把旧对象的内存复制到新对象) 方法将指向"a"的指针newv放到deque下标为1的位置。

    // deque[1] = "a"
    objc_memmove_collectable(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket));
    
    deque[1] = "a"

    #2.5: 更新元素个数,释放缓冲区等操作。

    cnt = 1;
    

    2.3数组中插入元素"b"

    CFArrayInsertValueAtIndex(mutableArray, 0, CFSTR("b"));
    

    #2.1:

    range = (0,0);
    newCount = 1;
    cnt = 1;
    futureCnt = 2;
    

    #2.3:

    调用 #3: __CFArrayRepositionDequeRegions方法 ,对 A、B、C 区域进行划分。

    #3.1:

    newCount = 1;
    L = 1;
    A = 0;
    B = 0;
    C = 1;
    R = 2;
    numNewElems = 1;
    
    wiggle = 4;
    

    #3.2: 空间紧凑或者不足的情况

    #3.2.1: 创建一个更大的newDeque, 对buckets进行扩容操作

    capacity = 12;
    oldL = 1;
    newL = 5;
    oldC0 = 1;
    newC0 = 6;
    newDeque->_leftIdx = 5;
    newDeque->_capacity = 12;
    

    C > 0: queue[1] --> newQueue[6]

    //移动C
    objc_memmove_collectable(newBuckets + newC0, buckets + oldC0, C * sizeof(struct __CFArrayBucket));
    
    queue[1] --> newQueue[6]

    #2.4: 移动B部分

    // deque[5] = "b"
    objc_memmove_collectable(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket));
    
    move B

    #2.5: 更新count属性, 释放缓冲区

    array->count = 2
    

    总结:

    1. 内部使用双端队列进行元素操作;
    2. 初始创建可变数组时,deque为null;
    3. deque 初始容量为 min(min(futureCnt * 2, long_max), 4), 最小为4, 最大为 long_max。一般为元素实际个数的2倍。
    4. deque 扩容容量为:min((futureCnt + 4) * 2, long_max)

    参考资料:

    相关文章

      网友评论

          本文标题:CFArray源码解读

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