OC中关于copy和mutableCopy

 这里主要有两个概念深拷贝和浅拷贝,主要涉及两个方法 - (id)copy;- (id)mutableCopy; 和一个属性修饰关键字@copy。

深拷贝 :即内容拷贝,指拷贝对象的具体内容,而内存地址是重新分配的,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉(对于集合类这里有点特殊,下面会详细讲述)。

浅拷贝:即指针拷贝,是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。

一、对象可以实现拷贝的前提

 在OC中,若要使对象可以拥有可拷贝操作,则该对象所属的类必须遵守NSCopyingNSMutableCopy协议,并重写copyWithZone:mutableCopyWithZone:方法。
 对于自定义类一般只实现NSCopying协议,同时如果遵循了NSCopying协议同时实现了copyWithZone:方法,则实例对象调用copy方法([animal copy])时会生成一个新的对象,值得注意的是这是一个深拷贝

  • 若自定义类直接继承于NSObject类,则不需要[super copyWithZone:zone] ;
  • 若自定义类的父类或者超类继承与NSObject,且其父类或超类均未遵循NSCopying协议,则不需要[super copyWithZone:zone] ;
  • 若自定义类的父类或者超类继承与NSObject,且其父类或超类遵循NSCopying协议,则需要[super copyWithZone:zone] ;
@interface Animal : NSObject
@property (nonatomic,copy) NSString * name;
@property (nonatomic,copy) NSString * age;
@end

@interface Animal()<NSCopying>

@end
// --------------------------------------------------------
@implementation Animal

- (id)copyWithZone:(NSZone *)zone
{
    Animal * animal = [[Animal allocWithZone:zone] init];
    animal.name = self.name;
    animal.age = self.age;
    return animal;
}
// -----------------------------------------------------
//在其他地方调用
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];

    Animal * animal = [[Animal alloc] init];
    animal.name = @"男儿何惜死";
    animal.age = @"27";
    Animal * copyAnimal = [animal copy];
    NSLog(@"animal.name/age = %@/%@, copyAnimal.name/age = %@/%@", animal.name, animal.age, copyAnimal.name, copyAnimal.age);
    NSLog(@"animal.address = %p, copyAnimal.address = %p", animal, copyAnimal);  
}
@end
二、Copy和MutableCopy的区别

  总的来说:如果不可变对象进行复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝。Copy 返回一个不可变对象的副本,MutalbeCopy返回一个可变对象的副本。至于深拷贝到什么地步,下面针对不同情况一一列举:

1、NSString、NSMutableString:
  • 普通字符串NSString/NSMutableString使用copy与mutableCopy的区别:
NSString * string = @"男儿何惜死";
NSString * copyString = [string copy];
NSMutableString * mutableCopyStr = [string mutableCopy];
NSLog(@"string = %p, copyStr = %p mutableCopyStr = %p", string, copyString, mutableCopyStr);

NSMutableString * mutableString = [NSMutableString stringWithString:@"破胆与君尝"];
NSString * copyMutableStr = [mutableString copy];
NSMutableString * mutableCopyMutStr = [mutableString mutableCopy];
NSLog(@"mutableString = %p, copyMutableStr = %p mutableCopyMutStr = %p", mutableString, copyMutableStr, mutableCopyMutStr);

// ------------------------------------------------
// 打印结果:验证了总结的结论
string = 0x10e1cf098, copyStr = 0x10e1cf098 mutableCopyStr = 0x600000442520
mutableString = 0x600000442550, copyMutableStr = 0x600000442580 mutableCopyMutStr = 0x6000004425b0
  • 对于NSString类来说当作为属性时,关键字@copy与@strong的区别:
    • 当赋予属性值为不可变字符串时,使用@copy和@strong关键字是没有区别的;
    • 当赋予属性值为可变字符串时,需要特别谨慎使用@copy和@strong;因为使用@strong修饰后属性指针与源指针指向同一块内存区域,当该内存中数据发生变化属性值也会变化,这可能不是我们想要看到的。
@property (nonatomic,copy) NSString * name;
@property (nonatomic,strong) NSString * strongName;
// ----------------------------------------------------
NSMutableString * mutableString = [NSMutableString stringWithString:@"男儿何惜死"];
self.name = mutableString;
self.strongName = mutableString;
NSLog(@"mutableString = %p, self.name = %p self.strongName = %p", mutableString, self.name, self.strongName);
NSLog(@"mutableString = %@, self.name = %@ self.strongName = %@", mutableString, self.name, self.strongName);
[mutableString appendString:@",破胆与君尝"];
NSLog(@"mutableString = %@, self.name = %@ self.strongName = %@", mutableString, self.name, self.strongName);

// ----------------------------------------------------
mutableString = 0x600000241a40, self.name = 0x600000241a70 self.strongName = 0x600000241a40
mutableString = 男儿何惜死, self.name = 男儿何惜死 self.strongName = 男儿何惜死
mutableString = 男儿何惜死,破胆与君尝, self.name = 男儿何惜死 self.strongName = 男儿何惜死,破胆与君尝
2、NSArray/NSMutableArray、NSDictionary/NSMutableDictionary、NSSet/NSMutableSet等集合类
(1、)对于常见使用@strong修饰的不可变集合:
@property (nonatomic,strong) NSArray * testArray;
// -------------------------------------------------------
// 示例一:赋予全部是不可变元素的不可变集合
self.testArray = @[@"不负光阴", @"不负卿"];
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"self.testArray = %p, test_1 = %p, test_2 = %p", self.testArray, test_1, test_2);
NSLog(@"\n self.testArray.firstObject = %p, self.testArray.lastObject = %p;
\n test_1.firstObject = %p, test_1.lastObject = %p;\n test_2.firstObject = %p, test_2.lastObject = %p",
 self.testArray[0], self.testArray[1], test_1[0], test_1[1], test_2[0], test_2[1]);
// ------------------------------------------------------
self.testArray = 0x604000039f60, test_1 = 0x604000039f60, test_2 = 0x60400025d130
self.testArray.firstObject = 0x10f61d0a8, self.testArray.lastObject = 0x10f61d0c8;
test_1.firstObject = 0x10f61d0a8, test_1.lastObject = 0x10f61d0c8;
test_2.firstObject = 0x10f61d0a8, test_2.lastObject = 0x10f61d0c8
// ---------------------------------------------------------
// 由上可知,虽然mutableCopy是深拷贝,但是不彻底,test_2中的元素与源数组元素依然指向同一块内存区域。
// 这在数组元素存在可变元素时会发生奇妙的事情。

// 示例二:赋予含有可变元素的不可变集合
self.testArray = @[[NSMutableString stringWithString:@"不负光阴"], @"不负卿"];
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"self.testArray = %@, test_1 = %@, test_2 = %@", self.testArray, test_1, test_2);
NSLog(@"self.testArray = %p, test_1 = %p, test_2 = %p", self.testArray, test_1, test_2);
NSLog(@"\n self.testArray.firstObject = %p, self.testArray.lastObject = %p;
\n test_1.firstObject = %p, test_1.lastObject = %p;\n test_2.firstObject = %p, test_2.lastObject = %p",
 self.testArray[0], self.testArray[1], test_1[0], test_1[1], test_2[0], test_2[1]);
NSMutableString * firstStr = [self.testArray firstObject];
[firstStr appendString:@",不负自己"];
NSLog(@"self.testArray = %@, test_1 = %@, test_2 = %@", self.testArray, test_1, test_2);
// --------------------------------------------------------
self.testArray =    (
    "不负光阴",
    "不负卿",
),
 test_1 =   (
    "不负光阴",
    "不负卿",
), 
test_2 =    (
    "不负光阴",
    "不负卿",
)

self.testArray = 0x60400022bf80, test_1 = 0x60400022bf80, test_2 = 0x604000445100
self.testArray.firstObject = 0x604000442430, self.testArray.lastObject = 0x1030c8df8;
 test_1.firstObject = 0x604000442430, test_1.lastObject = 0x1030c8df8;
 test_2.firstObject = 0x604000442430, test_2.lastObject = 0x1030c8df8

self.testArray =    (
    "不负光阴,不负自己",
    "不负卿",
), 
test_1 =    (
    "不负光阴,不负自己",
    "不负卿",
), 
test_2 =    (
    "不负光阴,不负自己",
    "不负卿",
)

// --------------------------------------------------------
// 可以看到所有数组的第一个元素全部发生了改变。同样数组内即使存在可变数元素,外部使用mutableCopy,内部元素依然指向同一块内存区域!
// 试想一下如果 将[firstStr appendString:@",不负自己"];
// 改为 firstStr = [NSMutableString stringWithString:@"不负光阴,不负自己"];
//打印结果如何?为什么?

// 示例三:赋予可变集合(这里与NSString相似)
NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
self.testArray = baseArray;
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"baseArray = %p, self.testArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testArray, test_1, test_2);
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
[baseArray addObject:@"不负自己"];
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
// ----------------------------------------------------------

baseArray = 0x6000004442f0, self.testArray = 0x6000004442f0, test_1 = 0x6000000342e0, test_2 = 0x600000444320
baseArray =     (
    "不负光阴",
    "不负卿",
), self.testArray =     (
    "不负光阴",
    "不负卿",
)
baseArray =     (
    "不负光阴",
    "不负卿",
    "不负自己",
), self.testArray =     (
    "不负光阴",
    "不负卿",
    "不负自己",
)
// ----------------------------------------------------------
// 赋值操作是一个浅拷贝的过程;
// 可以看到当源数据发生变化时self.testArray也与之变化,这可能也不是我们想看到到~

(2、)使用@copy关键字修饰的不可变集合:

 当赋予其不可变集合对象时与@strong没有区别,对其进行copy或者mutableCopy操作与(1、)中示例一、二情况一致,即copy操作生成的对象与原对象指向同一块内存区域,mutableCopy则不同;
 当对其赋予可变集合时,由于是@copy修饰,此时虽然是赋值操作同时也是一个深拷贝的过程,会在一个新的内存地址来存储@copy修饰的集合,集合内元素的地址与源集合中元素地址依然一样,但是此时再修改源集合,目标集合不会再改变!

@property (nonatomic,copy) NSArray * testCopyArray;
// -------------------------------------------------------------------
NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
self.testCopyArray = baseArray;
NSArray * test_1 = [self.testCopyArray copy];
NSMutableArray * test_2 = [self.testCopyArray mutableCopy];
NSLog(@"baseArray = %p, self.testCopyArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testCopyArray, test_1, test_2);
NSLog(@"baseArray = %@, self.testCopyArray = %@", baseArray, self.testCopyArray);
[baseArray addObject:@"不负自己"];
NSLog(@"baseArray = %@, self.testCopyArray = %@", baseArray, self.testCopyArray);
// -------------------------------------------------------------------
 baseArray = 0x60400025de80, self.testCopyArray = 0x60400022bb60, test_1 = 0x60400022bb60, test_2 = 0x60400025deb0
 baseArray =    (
    "不负光阴",
    "不负卿",
), self.testCopyArray =     (
    "不负光阴",
    "不负卿",
)
baseArray =     (
    "不负光阴",
    "不负卿",
    "不负自己",
), self.testCopyArray =     (
    "不负光阴",
    "不负卿",
)
// -------------------------------------------------------------------
// 是不是觉得使用@copy修饰的不可变集合更靠谱一些😂
// 同时需要注意的是:对于不可变数组,不管用什么修饰,不管给予其赋值的是可变还是不可变的数组,copy操作生成的对象与原对象均指向同一块内存区域!!!
(3、)使用@strong修饰的可变集合:
// 与(1、)中的第三种情况(示例三)相似
@property (nonatomic,strong) NSMutableArray * testArray;
 // ---------------------------------------------------------------
NSMutableArray * baseArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
self.testArray = baseArray;
NSArray * test_1 = [self.testArray copy];
NSMutableArray * test_2 = [self.testArray mutableCopy];
NSLog(@"baseArray = %p, self.testArray = %p, test_1 = %p, test_2 = %p", baseArray, self.testArray, test_1, test_2);
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
[baseArray addObject:@"不负自己"];
NSLog(@"baseArray = %@, self.testArray = %@", baseArray, self.testArray);
// -------------------------------------------------------------------
baseArray = 0x6000004496c0, self.testArray = 0x6000004496c0, test_1 = 0x6000000307c0, test_2 = 0x6000004496f0
baseArray =     (
    "不负光阴",
    "不负卿",
), self.testArray =     (
    "不负光阴",
    "不负卿",
)
baseArray =     (
    "不负光阴",
    "不负卿",
    "不负自己",
), self.testArray =     (
    "不负光阴",
    "不负卿",
    "不负自己",
)
(4、)使用@copy修饰的可变集合:
// 为其赋值等操作均没啥问题,问题出在将其作为一个值,赋予其他可变集合时!
@property (nonatomic,copy) NSMutableArray * testCopyArray;
// -------------------------------------------------------------------
self.testCopyArray = [NSMutableArray arrayWithArray:@[@"不负光阴", @"不负卿"]];
NSMutableArray * baseArray = [NSMutableArray array];
baseArray = self.testCopyArray;
NSLog(@"baseArray = %p, self.testCopyArray = %p", baseArray, self.testCopyArray);
[self.testCopyArray addObject:@"不负自己"];
[baseArray addObject:@"不负自己"];
// -------------------------------------------------------------------
//此时会崩溃
baseArray = 0x60400022acc0, self.testCopyArray = 0x60400022acc0
-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x60400022acc0

// 可以看到baseArray已经是一个不可变的集合,所以对其进行增删等操作会引发异常。
// 原因就是因为修饰词是@copy,我们上面已经强调了copy操作返回不可变对象,
// mutableCopy返回可变对象,这与是否是深拷贝与浅拷贝无关,要切记。

 以上均是用数组举例,其实字典等也相似的,可以自行尝试一下。这里给出一个简单的🌰

@property (nonatomic,strong) NSDictionary * testDic;
// ---------------------------------------------------------------------
NSMutableDictionary * dic = [NSMutableDictionary dictionaryWithDictionary:@{@"001" : @"男儿何惜死", @"002" : @"破胆与君尝"}];
self.testDic = dic;
NSDictionary * test_1 = [self.testDic copy];
NSMutableDictionary * test_2 = [self.testDic mutableCopy];
NSLog(@"dic = %p, self.testDic = %p, test_1 = %p, test_2 = %p", dic, self.testDic, test_1, test_2);
NSLog(@"dic = %@, self.testDic = %@", dic, self.testDic);
[dic setObject:@"两情若是久长时" forKey:@"003"];
NSLog(@"dic = %@, self.testDic = %@", dic, self.testDic);
// ---------------------------------------------------------------------
dic = 0x604000030320, self.testDic = 0x604000030320, test_1 = 0x604000030340, test_2 = 0x604000030360

dic =   {
    001 = "男儿何惜死",
    002 = "破胆与君尝",
}, self.testDic =   {
    001 = "男儿何惜死",
    002 = "破胆与君尝",
}

dic =   {
    001 = "男儿何惜死",
    002 = "破胆与君尝",
    003 = "两情若是久长时",
}, self.testDic =   {
    001 = "男儿何惜死",
    002 = "破胆与君尝",
    003 = "两情若是久长时",
}
三、集合的真正的深拷贝

 上面提到集合的深拷贝并不彻底,集合内的元素的地址依然是相同的,那么如何实现集合的真正的拷贝呢?参考文档

// 可以将该集合归档然后再解档,前提是内容均符合NSCoding协议,这样可以得到一个真正意义上的深拷贝。
@property (nonatomic,strong) NSArray * testArray;
// ---------------------------------------------------------------------
self.testArray = @[@"不负光阴", @"不负卿"];
NSArray * deepArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self.testArray]];
NSLog(@"self.testArray = %p, deepArray = %p", self.testArray, deepArray);
NSLog(@"self.testArray = %@, deepArray = %@", self.testArray, deepArray);
NSLog(@"self.testArray[0] = %p, self.testArray[1] = %p, deepArray[0] = %p, deepArray[1] = %p", self.testArray[0], self.testArray[1], deepArray[0], deepArray[1]);
// ---------------------------------------------------------------------
self.testArray = 0x604000229b60, deepArray = 0x604000229be0
self.testArray =    (
    "不负光阴",
    "不负卿",
), deepArray =  (
    "不负光阴",
    "不负卿",
)
self.testArray[0] = 0x10350d238, self.testArray[1] = 0x10350d258, deepArray[0] = 0x604000229c20, deepArray[1] = 0x604000229b80
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容