我们实际造了个分治数组
We are guessing Apple,but we don’t know the Apple is guessing us!
喔~我们又造了一个轮子,我们用分块的 NSMutableArray
造了一个类似的 CFStorage
的东东,目的在数据量大下数组的读写性能。在学校我们就知道 Array 的写操作是耗时的,因为挪动元素的时候要数组复制,数组读采用直接寻址,时间复杂度在 O(1)
,基于这样一个所谓的”印象”,我们在 App 中在存储海量数据时候使用 Array 就怀疑了,所以没有深入去研究 NSMutableArray
就使用了一个嵌套 Array 数组实现了一个可变数组,后来发现其实 NSMutableArray
完全满足我们的诉求,性能都不错,详情见这篇博文
于是找到NSMutableArray
的底层实现 CFArray.c 的实现。
struct __CFArray {
CFRuntimeBase _base;
CFIndex _count; /* number of objects */
CFIndex _mutations;
void *_store; /* can be NULL when MutableDeque */
};
struct __CFArrayDeque {
uint32_t _leftIdx;
uint32_t _capacity;
int32_t _bias;
#if __LP64__
uint32_t _pad; // GC: pointers must be 8-byte aligned for the collector to find them.
#endif
/* struct __CFArrayBucket buckets follow here */
};
struct __CFArrayBucket {
const void *_item;
};
CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks) {
CFAssert2(0 <= capacity, __kCFLogAssertion, "%s(): capacity (%d) cannot be less than zero", __PRETTY_FUNCTION__, capacity);
CFAssert2(capacity <= LONG_MAX / sizeof(void *), __kCFLogAssertion, "%s(): capacity (%d) is too large for this architecture", __PRETTY_FUNCTION__, capacity);
return (CFMutableArrayRef)__CFArrayInit(allocator, __kCFArrayDeque, capacity, callBacks); // Mutable 创建类型为 __kCFArrayDeque 的 Array
}
// 初始化数组
static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFArrayCallBacks *callBacks) {
struct __CFArray *memory;
...
size = __CFArrayGetSizeOfType(flags) - sizeof(CFRuntimeBase);
memory = (struct __CFArray*)_CFRuntimeCreateInstance(allocator, __kCFArrayTypeID, size, NULL);
if (NULL == memory) {
return NULL;
}
...
__CFArraySetCount((CFArrayRef)memory, 0);
switch (__CFBitfieldGetValue(flags, 1, 0)) {
case __kCFArrayDeque:
case __kCFArrayStorage:
if (__CFOASafe) __CFSetLastAllocationEventName(memory, "CFArray (mutable-variable)");
((struct __CFArray *)memory)->_mutations = 1;
((struct __CFArray*)memory)->_store = NULL;
break;
}
...
return (CFArrayRef)memory;
}
// 可变数组中插入新的 value
void CFArrayAppendValue(CFMutableArrayRef array, const void *value) {
CF_OBJC_FUNCDISPATCH1(__kCFArrayTypeID, void, array, "addObject:", value);
__CFGenericValidateType(array, __kCFArrayTypeID);
CFAssert1(__CFArrayGetType(array) != __kCFArrayImmutable, __kCFLogAssertion, "%s(): array is immutable", __PRETTY_FUNCTION__);
_CFArrayReplaceValues(array, CFRangeMake(__CFArrayGetCount(array), 0), &value, 1);
}
// 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
void _CFArrayReplaceValues(CFMutableArrayRef array, CFRange range, const void **newValues, CFIndex newCount) {
const CFArrayCallBacks *cb;
CFAllocatorRef allocator;
CFIndex idx, cnt, futureCnt;
const void **newv, *buffer[256];
cnt = __CFArrayGetCount(array);
futureCnt = cnt - range.length + newCount;
CFAssert1(newCount <= futureCnt, __kCFLogAssertion, "%s(): internal error 1", __PRETTY_FUNCTION__);
array->_mutations++;
} else { // Deque
// reposition regions A and C for new region B elements in gap
if (__CF_MAX_BUCKETS_PER_DEQUE <= futureCnt) {
CFStorageRef store;
__CFArrayConvertDequeToStore(array);
store = (CFStorageRef)array->_store;
if (range.length < newCount) {
CFStorageInsertValues(store, CFRangeMake(range.location + range.length, newCount - range.length));
} else if (newCount < range.length) { // this won't happen, but is here for completeness
CFStorageDeleteValues(store, CFRangeMake(range.location + newCount, range.length - newCount));
}
} else if (range.length != newCount) {
__CFArrayRepositionDequeRegions(array, range, newCount);
}
}
// copy in new region B elements
if (0 < newCount) {
if (__kCFArrayStorage == __CFArrayGetType(array)) {
CFStorageRef store = (CFStorageRef)array->_store;
CFStorageReplaceValues(store, CFRangeMake(range.location, newCount), newv);
} else { // Deque
struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store;
struct __CFArrayBucket *raw_buckets = (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque));
CFAllocatorRef bucketsAllocator = isStrongMemory(array) ? allocator : kCFAllocatorNull;
if (newCount == 1)
CF_WRITE_BARRIER_ASSIGN(bucketsAllocator, *((const void **)raw_buckets + deque->_leftIdx + range.location), newv[0]);
else
CF_WRITE_BARRIER_MEMMOVE(raw_buckets + deque->_leftIdx + range.location, newv, newCount * sizeof(struct __CFArrayBucket));
}
}
__CFArraySetCount(array, futureCnt);
if (newv != buffer && newv != newValues) CFAllocatorDeallocate(allocator, newv);
}
/* This shouldn't be called if the array count is 0. */
CF_INLINE struct __CFArrayBucket *__CFArrayGetBucketAtIndex(CFArrayRef array, CFIndex idx) {
switch (__CFArrayGetType(array)) {
case __kCFArrayImmutable:
case __kCFArrayDeque:
return __CFArrayGetBucketsPtr(array) + idx; // 调用 __CFArrayGetBucketsPtr 获取对应数组
case __kCFArrayStorage: {
CFStorageRef store = (CFStorageRef)array->_store;
return (struct __CFArrayBucket *)CFStorageGetValueAtIndex(store, idx, NULL);
}
}
return NULL;
}
/* Only applies to immutable and mutable-deque-using arrays;
* Returns the bucket holding the left-most real value in the latter case. */
CF_INLINE struct __CFArrayBucket *__CFArrayGetBucketsPtr(CFArrayRef array) {
switch (__CFArrayGetType(array)) {
case __kCFArrayImmutable:
return (struct __CFArrayBucket *)((uint8_t *)array + __CFArrayGetSizeOfType(((CFRuntimeBase *)array)->_cfinfo[CF_INFO_BITS]));
case __kCFArrayDeque: {
struct __CFArrayDeque *deque = (struct __CFArrayDeque *)array->_store;
return (struct __CFArrayBucket *)((uint8_t *)deque + sizeof(struct __CFArrayDeque) + deque->_leftIdx * sizeof(struct __CFArrayBucket));
}
}
return NULL;
}