一直對NSObject的copy方法似懂非懂,今天工作做完了,整理一下
深復制和淺復制是什么在這里就不贅述
今天主要分三種類型對copy進行探討:系統非容器類對象、系統容器類對象和自定義對象
系統非容器類對象(NSString,NSNumber等)
NSString *str1 = @"123"; NSString *str2 = [str1 copy]; NSString *str3 = [str1 mutableCopy]; NSMutableString *str4 = [str1 copy]; NSMutableString *str5 = [str1 mutableCopy]; // [str3 appendString:@"456"]; //編譯出錯 // [str4 appendString:@"456"]; //運行出錯
如上代碼,打印結果如下圖
結論:
1.對比所有string的地址,發現copy返回的是淺復制(只復制了指針),mutableCopy返回的是深復制(重新分配了內存)
2.str3在編譯時為NSString,運行時為NSMutableString類型,從圖中str3的類型為__NSCFString可證明,所以修改操作在編譯的時候就會報錯
3.str4在編譯時為NSMutableString,運行時為NSString類型,從圖中str4的類型為__NSCFConstantString可證明。所以對str4進行append等修改,編譯可通過,但運行時會直接crash
系統容器類對象(NSArray,NSDictionary等)
NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"1"], @"2", nil]; NSArray *array2 = [array1 copy]; NSArray *array3 = [array1 mutableCopy]; NSMutableArray *array4 = [array1 copy]; NSMutableArray *array5 = [array1 mutableCopy];
結論:
array1-array5的原理同NSString部分,但是注意mutableCopy返回的深復制,是對容器的深復制,容器里的元素仍然是淺復制
疑問:那如何對容器內的元素也進行深復制呢?
第一種方法:使用initWithArray:copyItems:方法
NSArray *array1 = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"1"], @"2", nil]; NSArray *array6 = [[NSArray alloc] initWithArray:array1 copyItems:YES];
此時array6的第一個元素是array1的第一個元素的深復制,但是第二個元素則是淺復制。
莫非用官方的api也會出錯?其實不是的,因為對於不可變對象,對其進行淺復制就足夠,因為你改變不了其值!
所以initWithArray:copyItems:可以實現一種不夠徹底的深復制
疑問:如果真的需要對容器中的不可變對象進行深復制,那怎么辦?
第二種方法:使用NSKeyedUnarchiver
NSArray *array7 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:array1]];
NSKeyedUnarchiver是真正意義上的深復制
自定義對象
以上,NSString和NSArray都是默認實現了NSCopying和NSMutableCopying協議,才能響應copy和mutableCopy方法
如果是自定義對象,那么怎么讓其響應copy和mutableCopy方法呢?
上代碼

// // MyObj.h // No // // Created by Norcy on 16/1/28. // Copyright © 2016年 Norcy. All rights reserved. // #import <Foundation/Foundation.h> @interface MyObj : NSObject<NSCopying, NSMutableCopying> @property (nonatomic, retain) NSMutableString *str1; @property (nonatomic, retain) NSString *str2; @property (nonatomic, assign) int num; @end

// // MyObj.m // No // // Created by Norcy on 16/1/28. // Copyright © 2016年 Norcy. All rights reserved. // #import "MyObj.h" @implementation MyObj - (id)init { if (self = [super init]) { self.str1 = [[NSMutableString alloc] init]; self.str2 = [[NSString alloc] init]; self.num = -1; } return self; } - (id)copyWithZone:(NSZone *)zone { MyObj *obj = [[[self class] allocWithZone:zone] init]; obj.str1 = [self.str1 copyWithZone:zone]; obj.str2 = [self.str2 copyWithZone:zone]; // obj.str1 = [self.str1 copy]; //這樣寫也可以 // obj.str2 = [self.str2 copy]; obj.num = self.num; return obj; } - (id)mutableCopyWithZone:(NSZone *)zone { MyObj *obj = [[[self class] allocWithZone:zone] init]; obj.str1 = [self.str1 mutableCopyWithZone:zone]; obj.str2 = [self.str2 mutableCopyWithZone:zone]; // obj.str1 = [self.str1 mutableCopy]; //這樣寫也可以 // obj.str2 = [self.str2 mutableCopy]; obj.num = self.num; return obj; } @end
調用方法如下:
MyObj *obj = [[MyObj alloc] init]; obj.str1 = @"123"; obj.str2 = @"345"; obj.num = 1; MyObj *obj2 = [obj copy]; MyObj *obj3 = [obj mutableCopy];
如上代碼,打印結果如下圖
可以看到
(1)copy方法准確返回了一個新的對象,且對象的屬性是淺復制
(2)mutableCopy方法准確返回了一個新的對象,且對象的屬性是深復制
(3)想要實現copy方法,需要讓對象聲明遵循<NSCopying>和<NSMutableCopying>協議,並且實現實現copyWithZone:和mutableCopyWithZone:方法
(注意哦,不是實現copy和mutableCopy方法;還有zone參數是什么鬼?其實zone參數的存在是歷史原因,現在我們不必太過關心它)
(4)以下這2種寫法在該情況下是等價的
obj.str1 = [self.str1 copyWithZone:zone]; obj.str2 = [self.str2 copyWithZone:zone];
// obj.str1 = [self.str1 copy]; //這樣寫也可以 // obj.str2 = [self.str2 copy];
參考文章: