strong
與copy
修飾符
strong
修飾的屬性,對該屬性賦值時發生指針拷貝,即淺拷貝;copy
修飾的屬性,對該屬性賦值時發生內容拷貝,即深拷貝。(存在特殊Case)
通過重寫對象的
setter
方法實現。
當將不可變對象賦值給
copy
修飾的不可變對象時,也是發生淺拷貝。這算是一種優化,驗證見下文。
對數組而言,淺拷貝即拷貝了指向數組的指針;深拷貝即拷貝了數組中存儲的內容,而這個深拷貝是通過調用copy
方法實現。
copy
與mutableCopy
方法
以下代碼發生崩潰,爲什麼?
@property (copy, nonatomic) NSMutableArray *copAry;
- (void)testCrash {
NSMutableArray *arr = [NSMutableArray arrayWithObjects:@1, @2, @3, nil];
self.copAry = arr;
[self.copAry removeObject:@1]; // 直接崩潰
NSLog(@"self.copyAry = %@", self.copAry);
}
arr
賦值給self.copAry
,依據上面原則,會調用copy
方法實現深拷貝。
注意: OC中不管是集合類對象(如:NSArray
、NSMutableArray
、NSSet
…)還是非集合類對象(如:NSString
…)接收到copy
和mutableCopy
消息時遵循以下原則:
copy
方法返回的都是不可變對象,因此如上代碼中,self.copAry
即爲不可變對象,對不可變對象調用可變對象的方法就會Crash;mutableCopy
方法返回的都是可變對象。
因此,如果我們使用
strong
來修飾self.copAry
,則在self.copAry = arr;
時發生淺拷貝,只是拷貝了arr
的指針,還是指向的原數組,還是可變對象。便不會發生Crash。
NSArray
爲什麼需要用copy
來修飾?
@property (strong, nonatomic) NSArray *arr1;
@property (copy, nonatomic) NSArray *arr2;
// 爲什麼 NSArray 需要使用 copy
- (void)testUseCopyWithAry {
NSMutableArray *arr = [NSMutableArray arrayWithObjects:@(1), @(2), @(3), nil];
self.arr1 = arr;
self.arr2 = arr;
[arr addObject:@(4)];
NSLog(@"arr1 = %@, arr2 = %@", self.arr1, self.arr2);
/* 輸出結果:
arr1 = (
1,
2,
3,
4
), arr2 = (
1,
2,
3
)
*/
}
如果對一個strong
修飾的NSArray
對象A賦值一個NSMutableArray
對象B,對於A對象而言它是不可變的,但是使用strong
修飾,賦值時發生了淺拷貝,A、B對象指向了同一個數組,因此對B的修改將會影響到A(如上代碼)。一般這並不是我們期望的,因此我們使用copy
來修飾NSArray
對象來避免上述情況。
對象間賦值時測試例子
- (void)test01 {
NSArray *array = @[@1,@2,@3,@4];
NSArray *copyArr = [array copy];
NSArray *mCopyArr = [array mutableCopy];
NSMutableArray *mcArr = [array copy];
NSMutableArray *mmCopyArr = [array mutableCopy];
NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);
/* 輸出結果
array: 0x60000024ce10
copyArr: 0x60000024ce10
mCopyArr: 0x60000024cd80
mcArr: 0x60000024ce10
mmCopyArr: 0x60000024ce70
*/
}
- (void)test02 {
NSArray *tarray = @[@1,@2,@3,@4];
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObjectsFromArray:tarray];
NSArray *copyArr = [array copy];
NSArray *mCopyArr = [array mutableCopy];
NSMutableArray *mcArr = [array copy];
NSMutableArray *mmCopyArr = [array mutableCopy];
NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);
/* 輸出結果
array: 0x60000024cd20
copyArr: 0x60000024cc90
mCopyArr: 0x60000024ce40
mcArr: 0x60000024cde0
mmCopyArr: 0x60000024d020
*/
}
總結:
- 對不可變對象(如:
NSArray
對象)發送copy
消息,返回不可變對象,這其中並沒有發生深拷貝,僅是拷貝了指針而已; - 對不可變對象發送
mutableCopy
消息,返回可變對象;對可變對象發送copy
消息,返回不可變對象;對可變對象發送mutableCopy
消息,返回可變對象,這其中均發生了深拷貝,即拷貝了對象的內容。
當然在 iOS 中並不是所有的對象都支持copy
,mutableCopy
,遵守 NSCopying
協議的類可以發送 copy
消息,遵守NSMutableCopying
協議的類纔可以發送 mutableCopy
消息。
假如發送了一個沒有遵守上訴兩協議而發送 copy
或者 mutableCopy
, 那麼就會發生異常。但是默認情況下iOS中的類並沒有遵守上述兩個協議。
如果想使用copy
那麼就必須遵守NSCopying
, 並且實現 copyWithZone:
方法,如果想使用mutableCopy
那麼就必須遵守 NSMutableCopying
, 並且實現 mutableCopyWithZone:
方法。