在iOS開發中,當提到深拷貝和淺拷貝的時候,大家都說懂,簡單;都會說,淺拷貝:指針(地址)拷貝,不會產生新對象;深拷貝:內容拷貝,會產生新對象,但當問及大家copy與mutableCopy的時候,他們各自是深拷貝還是淺拷貝的時候,誰又有底氣答對呢?下面一起研究下吧
NSString *string = @"string";
NSString *str1 = [string copy]; // 沒有產生新對象 【淺拷貝】
NSMutableString *str2 = [string mutableCopy]; // 產生新對象 【深拷貝】
NSLog(@"%p %p %p", string, [string copy], [string mutableCopy]);
打印結果如下:
根據打印結果解析:
當不可變字符串調用copy的時候,指針地址沒有發生改變,也就意味著沒有產生新的對象,所以屬於淺拷貝;
當不可變字符串調用mutableCopy的時候,指針地址發生了改變,意味著產生新的對象,所以屬於深拷貝。
NSMutableString *string = [NSMutableString string];
[string appendString:@"text1"];
NSString *str1 = [string copy]; // 產生新對象,變為不可變字符串【深拷貝】
NSMutableString *str2 = [string mutableCopy];// 產生新對象,還是可變字符串 【深拷貝】
[str2 appendString:@"text2"];
NSLog(@"%p %p %p", string, [string copy], [string mutableCopy]);
NSLog(@"%@ %@ %@", string, str1, str2);
打印結果如下:
根據打印結果解析:
當可變字符串調用copy的時候,指針地址發生了改變,也就意味著產生新的對象,所以屬於深拷貝;
當可變字符串調用mutableCopy的時候,指針地址發生了改變,意味著產生新的對象,所以屬於深拷貝。
NSArray *array = @[@"text1", @"text2"];
NSArray *array1 = [array copy]; // 沒有產生新對象 【淺拷貝】
NSMutableArray *array2 = [array mutableCopy]; // 產生新對象,變為可變數組【深拷貝】
[array2 addObject:@"text3"];
NSLog(@"%p %p %p", array, array1, array2);
NSLog(@"%@ %@ %@", array, array1, array2);
打印結果如下:
根據打印結果解析:
當不可變數組調用copy的時候,指針地址沒有發生改變,意味著沒有產生新的對象,所以屬於淺拷貝;
當不可變數組調用mutableCopy的時候,指針地址發生了改變,意味著產生新的對象,所以屬於深拷貝。
NSMutableArray *mutableArray = @[@"text1", @"text2"].mutableCopy;
NSArray *array1 = [mutableArray copy]; // 產生新對象,變為不可變數組【深拷貝】
NSMutableArray *mutableArray1 = [mutableArray mutableCopy]; // 產生新對象,變為可變數組 【深拷貝】
[mutableArray1 addObject:@"text3"];
NSLog(@"%p %p %p", mutableArray, [mutableArray copy], [mutableArray mutableCopy]);
NSLog(@"%@ %@ %@", mutableArray, array1, mutableArray1);
打印結果如下:
根據打印結果解析:
當可變數組調用copy的時候,指針地址發生了改變,也就意味著產生新的對象,所以屬於深拷貝;
當可變數組調用mutableCopy的時候,指針地址發生了改變,意味著產生新的對象,所以屬於深拷貝。
由於自定義對象不考慮可變,所以忽略mutableCopy
首先,當對象需要調用 copy 的時候,需要遵守遵守 NSCopying 協議 和 調用 copyWithZone:這個方法
@interface Dog : NSObject
/** 姓名 */
@property (nonatomic, copy) NSString *name;
/** 年齡 */
@property (nonatomic, assign) int age;
@end
// 需要遵守 NSCopying 協議
@interface Dog ()
@end
@implementation Dog
// 當對象需要調用 copy 的時候,需要調用 copyWithZone:這個方法
- (id)copyWithZone:(NSZone *)zone
{
Dog *dog = [[Dog allocWithZone:zone] init];
dog.name = self.name;
dog.age = self.age;
return dog;
}
@end
Dog *dog = [[Dog alloc] init]; // [object copyWithZone:zone]
dog.name = @"huahua";
dog.age = 1;
Dog *newDog = [dog copy]; // 產生新對象【深拷貝】
NSLog(@"%@ %@",dog,newDog);
NSLog(@"%@ %@",dog.name,newDog.name);
打印結果如下:
根據打印結果解析:
當自定義對象調用copy的時候,指針地址發生了改變,也就意味著產生新的對象,所以屬於深拷貝;
在平時定義屬性的時候,對於NSString 和 block 我們經常用 copy 來修飾
數組和字典等類型用 strong 來修飾;
當使用 copy 修飾屬性的時候,屬性的setter方法會調用[object copy]產生新的對象,
這樣,當原object對象的值發生改變時,並不影響新對象值;
// 定義NSString
@property(nonatomic, copy) NSString *name;
.
.
.
// 當調用上面的copy的時候,等價於下面的代碼
- (void)setName:(NSString *)name {
if (_name != name) {
[_name release];
_name = [name copy];
}
}
當使用 strong 修飾屬性的時候,屬性的setter方法會直接強引用該對象,
這樣,當原object對象的值發生改變時,新對象的屬性也改變;
例如:我們平時使用strong修飾的NSMutableArray,這個可變數組在當前文件中只有一個,而且是可變的;
/** 數組 */
@property(nonatomic,strong)NSMutableArray *array;
.
.
.
// 當調用上面的strong的時候,等價於下面的代碼
-(void)setArray:(NSMutableArray *)array{
_array = array;
}
// 定義一個可變的字符,作為小狗的name
NSMutableString *dogName = [NSMutableString stringWithString:@"huahua"]; // dogName == "huahua"
Dog *dog = [[Dog alloc] init];
// 將字符賦值給dog的name屬性
dog.name = dogName;
// 當小狗的name值發生改變時
[dogName appendString:@"lvlv"]; // dogName == "huahualvlv"
// 小狗的名還是原來的姓名
NSLog(@"%@",dog.name); // 打印結果:huahua
分析:當給dog.name 賦值時,會將 [dogName copy] 後的結果賦值給 dog.name,這樣,當dogName字符的值發生改變後,不會影響 dog.name 的值;
總結:當不可變類型對象調用copy拷貝後,不會產生新的對象,屬於淺拷貝,其他類型對象不管調用copy亦或是mutableCopy,都會產生新的對象,屬於深拷貝!