久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

iOS 中 copy 的原理

 泰榮林黑皮 2020-07-19

Python實(shí)戰(zhàn)社群

Java實(shí)戰(zhàn)社群

長按識(shí)別下方二維碼,按需求添加

掃碼關(guān)注添加客服

進(jìn)Python社群▲

掃碼關(guān)注添加客服

進(jìn)Java社群

作者丨伯陽

來源丨知識(shí)小集(zsxjtip)


前幾天在群里和朋友討論起 copy 的各種細(xì)節(jié),,發(fā)現(xiàn)有些地方依然模糊不清,,便查閱文檔,,翻看源碼,,以解心中之惑,。

本文測試代碼請點(diǎn)擊 https://github.com/BiBoyang/BoyangBlog/tree/master/CopyTest

屬性中的 copy

我們知道,屬性中的 copy,,其實(shí)是不保留新值的,,而是將其拷貝一份,,當(dāng)使用不可變對象的時(shí)候,,可以用它來保護(hù)封裝性,確保它不會(huì)隨意的變動(dòng)。那么它是如何實(shí)現(xiàn)的呢,?

在 objc-accessor.mm 中,,有 property 中 copy 的實(shí)現(xiàn)。

這里有兩個(gè)函數(shù) objc_copyStruct 和 objc_copyCppObjectAtomic,分別對應(yīng)結(jié)構(gòu)體的拷貝和對象的拷貝,。具體代碼如下:

/**
* 結(jié)構(gòu)體拷貝
* src:源指針
* dest:目標(biāo)指針
* size:大小
* atomic:是否是原子操作
* hasStrong:可能是表示是否是strong修飾
*/

void objc_copyStruct(void *dest, const void *src, ptrdiff_t size, BOOL atomic, BOOL hasStrong __unused) {
spinlock_t *srcLock = nil;
spinlock_t *dstLock = nil;
// >> 如果是原子操作,,則加鎖
if (atomic) {
srcLock = &StructLocks[src];
dstLock = &StructLocks[dest];
spinlock_t::lockTwo(srcLock, dstLock);
}
// >> 實(shí)際的拷貝操作
memmove(dest, src, size);

// >> 解鎖
if (atomic) {
spinlock_t::unlockTwo(srcLock, dstLock);
}
}

/**
* 對象拷貝
* src:源指針
* dest:目標(biāo)指針
* copyHelper:對對象進(jìn)行實(shí)際拷貝的函數(shù)指針,參數(shù)是src和dest
*/


void objc_copyCppObjectAtomic(void *dest, const void *src, void (*copyHelper) (void *dest, const void *source)) {
// >> 獲取源指針的對象鎖
spinlock_t *srcLock = &CppObjectLocks[src];
// >> 獲取目標(biāo)指針的對象鎖
spinlock_t *dstLock = &CppObjectLocks[dest];
// >> 對源對象和目標(biāo)對象進(jìn)行上鎖
spinlock_t::lockTwo(srcLock, dstLock);

// let C++ code perform the actual copy.
// >> 調(diào)用函數(shù)指針對應(yīng)的函數(shù),,讓C++進(jìn)行實(shí)際的拷貝操作
copyHelper(dest, src);
// >> 解鎖
spinlock_t::unlockTwo(srcLock, dstLock);
}

從上述代碼中,,我們可以得出結(jié)論:

  1. 對結(jié)構(gòu)體進(jìn)行 copy,直接對結(jié)構(gòu)體指針?biāo)赶虻膬?nèi)存進(jìn)行拷貝即可,;

  2. 對對象進(jìn)行 copy,,則會(huì)傳入的源指針和目標(biāo)對象同時(shí)進(jìn)行加鎖,然后在去進(jìn)行拷貝操作,,所以我們可以知道,,copy 操作是線程安全的。

不過這里的 copyHelper(dest, src);找不到實(shí)現(xiàn)方法,,則有些遺憾,。

深淺拷貝

  • 淺拷貝:只創(chuàng)建一個(gè)新的指針,指向原指針指向的內(nèi)存,;

  • 深拷貝:創(chuàng)建一個(gè)新的指針,,并開辟新的內(nèi)存空間,內(nèi)容拷貝自原指針指向的內(nèi)存,,并指向它,。

我們分別使用 copy 和 strong,對 NSString 和 NSMutableString進(jìn)行兩兩分配,;以及對 NSArray 和 NSMutableArray 進(jìn)行兩兩分配,,可以得到一個(gè)結(jié)果。

測試的源碼在這里 https://github.com/BiBoyang/BoyangBlog/blob/master/CopyTest/CopyTest/ViewController.m ,,可以查看測試的代碼,。

通過一系列測試,我得到了一個(gè)這樣的結(jié)論,。

非容器對象:

可不可變對象copy類型深淺拷貝返回對象是否可變
不可變對象copy淺拷貝不可變
可變對象copy深拷貝不可變
不可變對象mutableCopy深拷貝可變
可變對象mutableCopy深拷貝可變
  • 這里的源字符串如果是 Tagged Pointer 類型,,即 NSTaggedPointerString,會(huì)有些有趣的情況,,不過并不影響結(jié)果,。可以在文章末尾查看,。

  • 注意:接收 copy 結(jié)果的對象,,也需要是可變的并且屬性關(guān)鍵字是 strong,,才可以進(jìn)行修改,也就是可變,,兩個(gè)條件一個(gè)不符合則無法改變,。

容器對象:

可不可變對象copy類型深淺拷貝返回對象是否可變內(nèi)部元素信息Info
不可變對象copy淺拷貝不可變內(nèi)部元素是淺拷貝集合地址不變
可變對象copy淺拷貝不可變內(nèi)部元素是淺拷貝集合地址改變
看起來是深拷貝,實(shí)際不是
不可變對象mutableCopy淺拷貝可變內(nèi)部元素是淺拷貝集合地址改變
看起來是深拷貝,,實(shí)際不是
可變對象mutableCopy淺拷貝可變內(nèi)部元素是淺拷貝集合地址改變
看起來是深拷貝,,實(shí)際不是
  • 除了不可變對象使用 copy,其他的 copy 和 mutableCopy,,都是開辟了一個(gè)新的集合空間,,但是內(nèi)部的元素的指針還是指向源地址;

  • 有的人將集合地址改變的拷貝稱之為深拷貝,,但是這個(gè)其實(shí)是非常錯(cuò)誤的理解,,深拷貝就是全層次的拷貝。

從源碼中探究答案

上面的測試更多的是我們自己去一個(gè)一個(gè)的測試,,更底層的實(shí)現(xiàn)原理,,還是要看源碼。

對于字符串,,我們雖然因?yàn)?Foundation.framework 并未開源找不到源碼,,但是我們依舊可以去查閱開源的 CoreFoundation.framework 源碼。因?yàn)?CoreFoundation 和 Foundation 的對象是 Toll-free bridge 的,,所以,,可以從 CoreFoundation 的源代碼進(jìn)行了解。

我們進(jìn)入到這里查閱相關(guān)代碼 CFString.h,,里面給出了 CFStringCreateCopy 和 CFStringCreateMutableCopy 這兩個(gè)方法,,分別對應(yīng) copy 和 mutableCopy;以及 CFArray.c,,里面給出了 CFArrayCreateCopy 和 CFArrayCreateMutableCopy 兩個(gè)方法,,分別對應(yīng) copy 和 mutableCopy。

CFStringCreateCopy

/**
* >> 字符串的 copy 操作
*/

CFStringRef CFStringCreateCopy(CFAllocatorRef alloc, CFStringRef str) {
// CF_OBJC_FUNCDISPATCHV(__kCFStringTypeID, CFStringRef, (NSString *)str, copy);

/*
* 如果該字符串不是可變的,,并且與我們使用的分配器具有相同的分配器,,
并且這些字符是內(nèi)聯(lián)的,或者由該字符串擁有,,或者該字符串是常量,。
然后保留而不是制作真實(shí)副本。
*/

__CFAssertIsString(str);
// >> 判斷源字符串是否是 mutable
if (!__CFStrIsMutable((CFStringRef)str) && // If the string is not mutable
((alloc ? alloc : __CFGetDefaultAllocator()) == __CFGetAllocator(str)) && // and it has the same allocator as the one we're using
(__CFStrIsInline((CFStringRef)str) || __CFStrFreeContentsWhenDone((CFStringRef)str) || __CFStrIsConstant((CFStringRef)str))) { // and the characters are inline, or are owned by the string, or the string is constant
// >> 使用引用計(jì)數(shù)加一來代替真正的copy,也就是這里是淺拷貝,。
if (!(kCFUseCollectableAllocator && (0))) CFRetain(str); // Then just retain instead of making a true copy
return str;
}


if (__CFStrIsEightBit((CFStringRef)str)) {
const uint8_t *contents = (const uint8_t *)__CFStrContents((CFStringRef)str);
return __CFStringCreateImmutableFunnel3(alloc, contents + __CFStrSkipAnyLengthByte((CFStringRef)str), __CFStrLength2((CFStringRef)str, contents), __CFStringGetEightBitStringEncoding(), false, false, false, false, false, ALLOCATORSFREEFUNC, 0);
} else {
const UniChar *contents = (const UniChar *)__CFStrContents((CFStringRef)str);
return __CFStringCreateImmutableFunnel3(alloc, contents, __CFStrLength2((CFStringRef)str, contents) * sizeof(UniChar), kCFStringEncodingUnicode, false, true, false, false, false, ALLOCATORSFREEFUNC, 0);
}
}

從上面代碼我們可以得到幾條信息:

  1. CFStringCreateCopy 函數(shù),,返回的字符串是否返回新的對象,要看源字符串是 immutable 還是 mutable 的,;

  2. 如果源字符串是 mutable 的,,會(huì)開辟一片新的內(nèi)存,,生成一個(gè)新的 immutable 對象返回,創(chuàng)建使用 __CFStringCreateImmutableFunnel3 方法,,這個(gè)是深拷貝

  3. 如果源字符串是 immutable 的,,且是內(nèi)聯(lián)的,、string 所持有或者是常量,那么只對源 CFStringRef 對象引用計(jì)數(shù)加一,,這個(gè)是淺拷貝,。

CFStringCreateMutableCopy

/*
* >>>> 字符串的 copy 操作
*/

CFMutableStringRef CFStringCreateMutableCopy(CFAllocatorRef alloc, CFIndex maxLength, CFStringRef string) {
CFMutableStringRef newString;

// CF_OBJC_FUNCDISPATCHV(__kCFStringTypeID, CFMutableStringRef, (NSString *)string, mutableCopy);

__CFAssertIsString(string);

newString = CFStringCreateMutable(alloc, maxLength);
// 將源對象的內(nèi)容,放到新創(chuàng)建的newString中
__CFStringReplace(newString, CFRangeMake(0, 0), string);

return newString;
}

在這里,,可以知道,,在 CFStringCreateMutableCopy 函數(shù)里,不再需要判斷源對象是否是 mutable,,直接創(chuàng)建一個(gè)新的對象,,然后將源內(nèi)容拷貝一份放到新的對象里,這里也就是深拷貝,。

CFArrayCreateCopy

CFArrayRef CFArrayCreateCopy(CFAllocatorRef allocator, CFArrayRef array) {
return __CFArrayCreateCopy0(allocator, array);
}

CF_PRIVATE CFArrayRef __CFArrayCreateCopy0(CFAllocatorRef allocator, CFArrayRef array) {

CFArrayRef result;
// >>>> CFArrayCallBacks變量,,用于存放數(shù)組元素的回調(diào)
const CFArrayCallBacks *cb;

// >>>> 存放數(shù)組元素的結(jié)構(gòu)體指針
struct __CFArrayBucket *buckets;
CFAllocatorRef bucketsAllocator;
void* bucketsBase;

// >>>> 獲取源數(shù)組元素的總個(gè)數(shù)
CFIndex numValues = CFArrayGetCount(array);
CFIndex idx;
if (CF_IS_OBJC(CFArrayGetTypeID(), array)) {
cb = &kCFTypeArrayCallBacks;
} else {
cb = __CFArrayGetCallBacks(array);
}

// >>>> 初始化以一個(gè)不可變數(shù)組
result = __CFArrayInit(allocator, __kCFArrayImmutable, numValues, cb);
cb = __CFArrayGetCallBacks(result); // GC: use the new array's callbacks so we don't leak.
buckets = __CFArrayGetBucketsPtr(result);
bucketsAllocator = isStrongMemory(result) ? allocator : kCFAllocatorNull;
bucketsBase = CF_IS_COLLECTABLE_ALLOCATOR(bucketsAllocator) ? (void *)auto_zone_base_pointer(objc_collectableZone(), buckets) : NULL;
for (idx = 0; idx < numValues; idx++) {
const void *value = CFArrayGetValueAtIndex(array, idx);
if (NULL != cb->retain) {
value = (void *)INVOKE_CALLBACK2(cb->retain, allocator, value);
}
__CFAssignWithWriteBarrier((void **)&buckets->_item, (void *)value);
buckets++;
}

// >>>> //設(shè)定數(shù)組的長度count
__CFArraySetCount(result, numValues);
return result;
}

CFArrayCreateMutableCopy

CFMutableArrayRef CFArrayCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFArrayRef array) {
return __CFArrayCreateMutableCopy0(allocator, capacity, array);
}

CF_PRIVATE CFMutableArrayRef __CFArrayCreateMutableCopy0(CFAllocatorRef allocator, CFIndex capacity, CFArrayRef array) {
CFMutableArrayRef result;
const CFArrayCallBacks *cb;
CFIndex idx, numValues = CFArrayGetCount(array);
UInt32 flags;
if (CF_IS_OBJC(CFArrayGetTypeID(), array)) {
cb = &kCFTypeArrayCallBacks;
}
else {
cb = __CFArrayGetCallBacks(array);
}
// 將標(biāo)記設(shè)置為雙端隊(duì)列
flags = __kCFArrayDeque;
// 創(chuàng)建新的不可變數(shù)組
result = (CFMutableArrayRef)__CFArrayInit(allocator, flags, capacity, cb);
// 設(shè)置數(shù)組的容量
if (0 == capacity) _CFArraySetCapacity(result, numValues);

for (idx = 0; idx < numValues; idx++) {
const void *value = CFArrayGetValueAtIndex(array, idx);
// 將元素對象添加到新的數(shù)組列表中
CFArrayAppendValue(result, value);
}
return result;
}

從上面兩份代碼,我們可以可以:
1. immutable 和 mutable 數(shù)組的拷貝,,都是會(huì)調(diào)用 __CFArrayInit 函數(shù)去創(chuàng)建一個(gè)新的對象,;
2. 內(nèi)部元素實(shí)際上還都是指向源數(shù)組
3. 不可變數(shù)組的 copy 沒有體現(xiàn)在代碼中,,個(gè)人猜測可能是實(shí)現(xiàn)過于簡單,,所以也就沒有在這里實(shí)現(xiàn)。

其他的容器,,類似 CFDictionary,、CFSet 等,也是類似的結(jié)果,。

真正的深拷貝

可以發(fā)現(xiàn),,其實(shí)如果嚴(yán)格按照深拷貝的定義,集合的 copy 和 mutableCopy 其實(shí)都是淺拷貝,。那么該如何實(shí)現(xiàn)真正的深拷貝呢,?有兩個(gè)辦法:

一. 使用對象的序列化拷貝:

//數(shù)組內(nèi)對象是指針復(fù)制
NSArray *deepCopyArray = [[NSArray alloc] initWithArray:array];
//真正意義上的深復(fù)制,數(shù)組內(nèi)對象是對象復(fù)制
NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:array]];

二. 自己實(shí)現(xiàn) copy 協(xié)議

也就是 NSCopying,NSMutableCopying,。

特殊情況

在測試的時(shí)候,,發(fā)現(xiàn)如果這個(gè)字符串是 isTaggedPointerString ,則有個(gè)特殊情況,,不過貌似也沒什么用處,。

可不可變對象copy類型深淺拷貝接收對象關(guān)鍵字返回對象是否可變
不可變對象copy淺拷貝
不可變
可變對象copy深拷貝
不可變
不可變對象mutableCopy深拷貝strong可變
不可變對象mutableCopy淺拷貝copy不可變
可變對象mutableCopy深拷貝strong可變
可變對象mutableCopy深拷貝copy不可變


參考

[1]https://opensource.apple.com/source/CF/CF-1151.16/
[2]https://opensource.apple.com/tarballs/CF/
[3]https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Collections/Articles/Copying.html#//apple_ref/doc/uid/TP40010162-SW3
[4]https://en./wiki/Object_copying#Deep_copy
[5]https:///questions/184710/what-is-the-difference-between-a-deep-copy-and-a-shallow-copy

程序員專欄

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多