iOS Objective-C 深入理解Copy
copy
如果是不可變的值,行為與strong相同。
如果是可變的值,會將一個副本賦給例項變數。當一個不可變類有一個可變的子類時
(NSString NSMutableString,NSArray NSMutableArray)可以防止setter 方法傳遞一個
可變的子類的物件。會導致我們在不知情的情況下修改物件的值。
經常在哪裡使用
- NSString、NSArray、NSDictionary 等等經常使用copy關鍵字.
- block
為什麼?
- NSString,NSArray,NSDictionary 有對應的可變型別。NSMutableString、NSMutableArray、NSMutableDictionary
因為父類指標可以指向子類物件,使用copy的目的是為了讓本物件的屬性不受外界影響,使用copy無論傳給我的是一個可變物件還是不可變物件,我本身持有的是不可變得副本
- block 使用 copy 是從 MRC 遺留下來的“傳統”,在 MRC 中,方法內部的 block 是在棧區的,使用 copy 可以把它放到堆區.在 ARC 中寫不寫都行:對於 block 使用 copy 還是 strong 效果是一樣的。
例題:
用@property宣告的NSString(或NSArray,NSDictionary)經常使用copy關鍵字,為什麼?如果改用strong關鍵字,可能造成什麼問題?
因為父類指標可以指向子類物件,使用 copy 的目的是為了讓本物件的屬性不受外界影響,使用 copy 無論給我傳入是一個可變物件還是不可物件,我本身持有的就是一個不可變的副本.
如果我們使用是 strong ,那麼這個屬性就有可能指向一個可變物件,如果這個可變物件在外部被修改了,那麼會影響該屬性.
copy 此特質所表達的所屬關係與 strong 類似。然而設定方法並不保留新值,而是將其“拷貝” (copy)。 當屬性型別為 NSString 時,經常用此特質來保護其封裝性,因為傳遞給設定方法的新值有可能指向一個 NSMutableString 類的例項。這個類是 NSString 的子類,表示一種可修改其值的字串,此時若是不拷貝字串,那麼設定完屬性之後,字串的值就可能會在物件不知情的情況下遭人更改。所以,這時就要拷貝一份“不可變” (immutable)的字串,確保物件中的字串值不會無意間變動。只要實現屬性所用的物件是“可變的” (mutable),就應該在設定新屬性值時拷貝一份。
@property (nonatomic ,readwrite, strong) NSArray *array;
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
NSArray *array = @[ @1, @2, @3, @4 ];
self.array = mutableArray;
[mutableArray removeAllObjects];;
NSLog(@"%@",self.array);
[mutableArray addObjectsFromArray:array];
self.array = [mutableArray copy];
[mutableArray removeAllObjects];;
NSLog(@"%@",self.array);
//列印結果
2015-09-27 19:10:32.523 CYLArrayCopyDmo[10681:713670] (
)
2015-09-27 19:10:32.524 CYLArrayCopyDmo[10681:713670] (
1,
2,
3,
4
)
集合類和非集合類的copy操作
系統物件的copy與mutableCopy方法
- copy返回imutable物件;所以,如果對copy返回值使用mutable物件介面就會crash;
- mutableCopy返回mutable物件;
(無論是否可變都是一個新的物件)
對非集合類物件的copy操作:
在非集合類物件中:對 immutable 物件進行 copy 操作,是指標複製,mutableCopy 操作時內容複製;對 mutable 物件進行 copy 和 mutableCopy 都是內容複製。用程式碼簡單表示如下:
[immutableObject copy] // 淺複製
[immutableObject mutableCopy] //深複製
[mutableObject copy] //深複製
[mutableObject mutableCopy] //深複製
比如以下程式碼:
NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];
檢視記憶體,會發現 string、stringCopy 記憶體地址都不一樣,說明此時都是做內容拷貝、深拷貝。即使你進行如下操作:
[string appendString:@"origion!"]
stringCopy 的值也不會因此改變,但是如果不使用 copy,stringCopy 的值就會被改變。 集合類物件以此類推。 所以,用 @property 宣告 NSString、NSArray、NSDictionary 經常使用 copy 關鍵字,是因為他們有對應的可變型別:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操作,為確保物件中的字串值不會無意間變動,應該在設定新屬性值時拷貝一份。
集合類物件的copy與mutableCopy
集合類物件是指 NSArray、NSDictionary、NSSet … 之類的物件。下面先看集合類immutable物件使用 copy 和 mutableCopy 的一個例子:
NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
檢視內容,可以看到 copyArray 和 array 的地址是一樣的,而 mCopyArray 和 array 的地址是不同的。說明 copy 操作進行了指標拷貝,mutableCopy 進行了內容拷貝。但需要強調的是:此處的內容拷貝,僅僅是拷貝 array 這個物件,array 集合內部的元素仍然是指標拷貝。這和上面的非集合 immutable 物件的拷貝還是挺相似的,那麼mutable物件的拷貝會不會類似呢?我們繼續往下,看 mutable 物件拷貝的例子:
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];
檢視記憶體,如我們所料,copyArray、mCopyArray和 array 的記憶體地址都不一樣,說明 copyArray、mCopyArray 都對 array 進行了內容拷貝。同樣,我們可以得出結論:
在集合類物件中,對 immutable 物件進行 copy,是指標複製, mutableCopy 是內容複製;對 mutable 物件進行 copy 和 mutableCopy 都是內容複製。但是:集合物件的內容複製僅限於物件本身,物件元素仍然是指標複製。用程式碼簡單表示如下:
[immutableObject copy] // 淺複製
[immutableObject mutableCopy] //單層深複製
[mutableObject copy] //單層深複製
[mutableObject mutableCopy] //單層深複製
這個程式碼結論和非集合類的非常相似。
幾種複製的區分
- 淺複製(shallow copy):在淺複製操作時,對於被複制物件的每一層都是指標複製。
- 深複製(one-level-deep copy):在深複製操作時,對於被複制物件,至少有一層是深複製。
- 完全複製(real-deep copy):在完全複製操作時,對於被複制物件的每一層都是物件複製
這個寫法會出什麼問題: @property (copy) NSMutableArray *array;
- 新增,刪除,修改陣列內的元素的時候,程式會因為找不到對應的方法而崩潰.因為 copy 就是複製一個不可變 NSArray 的物件;
- 使用了 atomic 屬性會嚴重影響效能
(atomic並不能保證執行緒安全,若要實現執行緒安全的操作,還需要更為深層的鎖定機制。)