深刻理解iOS中的“深拷贝”和“浅拷贝”

  • 浅拷贝就是拷贝后,并没有进行真正的复制,而是复制的对象和原对象都指向同一个地址
  • 深拷贝是真正的复制了一份,复制的对象指向了新的地址
浅拷贝 < ----- > 深拷贝

从上图可以看出,浅拷贝A指针改变了所指向的内容B指针也指向被修改后的内容。如果有些地方用到B指针,不希望在A指向的内容发生变化时也跟着变化,则需要用到深拷贝。
通俗理解为:浅拷贝好比你的影子,你死了,影子也没了;深拷贝好比克隆人,你死了,它还在。

深拷贝和浅拷贝的本质是地址是否相同

在开发过程中,大体上会区分为对象和容器两个概念,对象的copy是浅拷贝,mutablecopy是深拷贝。

容器包含对象的拷贝,无论是copy,还是mutablecopy都是浅拷贝,要想实现对象的深拷贝,必须自己提供拷贝方法

  1. 非容器不可变对象:NSString
  2. 费容器可变对象: NSMutableString
  3. 容器类不可变对象: NSArray
  4. 容器类可变对象: NSMutableArray

代码测试

非容器不可变对象

    NSString *str1 = @"非容器不可变对象";
    NSString *str2 = [str1 copy];
    NSString *str3 = [str1 mutableCopy];
    
    NSLog(@"str1_p : %p, class: %@", str1, [str1 class]);
    NSLog(@"str2_p : %p, class: %@", str2, [str2 class]);
    NSLog(@"str3_p : %p, class: %@", str3, [str3 class]);

输出:

str1_p : 0x104c4a068, class: __NSCFConstantString
str2_p : 0x104c4a068, class: __NSCFConstantString
str3_p : 0x61000006db00, class: __NSCFString

结论:

  1. 对于非容器不可变对象的copy为浅拷贝,mutableCopy为深拷贝
  2. 浅拷贝获得的对象地址和原对象地址一致, 返回的对象为不可变对象
  3. 深拷贝返回新的内存地址,返回对象为可变对象

非容器可变对象

    NSMutableString *str1 = [NSMutableString stringWithFormat:@"非容器可变对象"];
    NSMutableString *str2 = [str1 copy];
    NSMutableString *str3 = [str1 mutableCopy];
    
    NSLog(@"str1_p : %p, class: %@", str1, [str1 class]);
    NSLog(@"str2_p : %p, class: %@", str2, [str2 class]);
    NSLog(@"str3_p : %p, class: %@", str3, [str3 class]);

输出:

str1_p : 0x618000260140, class: __NSCFString
str2_p : 0x61800004efd0, class: __NSCFString
str3_p : 0x6180002602c0, class: __NSCFString

结论:

  1. 对于非容器可变对象的copy为深拷贝
  2. mutableCopy为深拷贝
  3. 并且copy和mutableCopy返回对象都为可变对象

容器不可变对象

    NSMutableString *str1 = [NSMutableString stringWithFormat:@"非容器可变对象"];
    
    NSArray *array = [NSArray arrayWithObjects:str1, @"非容器不可变对象", nil];
    NSArray *copyArray = [array copy];
    NSArray *mutableCopyArray = [array mutableCopy];
    
    NSLog(@"array_p: %p, class: %@", array, [array class]);
    NSLog(@"copyArray_p: %p, class: %@", copyArray, [copyArray class]);
    NSLog(@"mutableCopyArray_p: %p, class: %@", mutableCopyArray, [mutableCopyArray class]);
    
    NSLog(@"======原对象=====");
    NSLog(@"object_p: %p, class: %@", array[0], [array[0] class]);
    NSLog(@"object_p: %p, class: %@", array[1], [array[1] class]);
    
    NSLog(@"======copy对象=====");
    NSLog(@"object_p: %p, class: %@", copyArray[0], [copyArray[0] class]);
    NSLog(@"object_p: %p, class: %@", copyArray[1], [copyArray[1] class]);
    
    NSLog(@"======mutableCopy对象=====");
    NSLog(@"object_p: %p, class: %@", mutableCopyArray[0], [mutableCopyArray[0] class]);
    NSLog(@"object_p: %p, class: %@", mutableCopyArray[1], [mutableCopyArray[1] class]);

输出:

array_p: 0x610000024ac0, class: __NSArrayI
copyArray_p: 0x610000024ac0, class: __NSArrayI
mutableCopyArray_p: 0x610000242610, class: __NSArrayM

======原对象=====
object_p: 0x61000006f540, class: __NSCFString
object_p: 0x10c386080, class: __NSCFConstantString

======copy对象=====
object_p: 0x61000006f540, class: __NSCFString
object_p: 0x10c386080, class: __NSCFConstantString

======mutableCopy对象=====
object_p: 0x61000006f540, class: __NSCFString
object_p: 0x10c386080, class: __NSCFConstantString

结论:
从上述输出中可以看出容器类不可变对象mutableCopy确实返回一个新的容器,但容器内的元素仍然是浅拷贝

容器可变对象

    NSMutableString *str1 = [NSMutableString stringWithFormat:@"非容器可变对象"];
    
    NSMutableArray *array = [NSMutableArray arrayWithObjects:str1, @"非容器不可变对象", nil];
    NSMutableArray *copyArray = [array copy];
    NSMutableArray *mutableCopyArray = [array mutableCopy];
    
    NSLog(@"array_p: %p, class: %@", array, [array class]);
    NSLog(@"copyArray_p: %p, class: %@", copyArray, [copyArray class]);
    NSLog(@"mutableCopyArray_p: %p, class: %@", mutableCopyArray, [mutableCopyArray class]);
    
    NSLog(@"======原对象=====");
    NSLog(@"object_p: %p, class: %@", array[0], [array[0] class]);
    NSLog(@"object_p: %p, class: %@", array[1], [array[1] class]);
    
    NSLog(@"======copy对象=====");
    NSLog(@"object_p: %p, class: %@", copyArray[0], [copyArray[0] class]);
    NSLog(@"object_p: %p, class: %@", copyArray[1], [copyArray[1] class]);
    
    NSLog(@"======mutableCopy对象=====");
    NSLog(@"object_p: %p, class: %@", mutableCopyArray[0], [mutableCopyArray[0] class]);
    NSLog(@"object_p: %p, class: %@", mutableCopyArray[1], [mutableCopyArray[1] class]);

输出:

array_p: 0x6100000552d0, class: __NSArrayM
copyArray_p: 0x610000031340, class: __NSArrayI
mutableCopyArray_p: 0x610000055000, class: __NSArrayM

======原对象=====
object_p: 0x610000267300, class: __NSCFString
object_p: 0x107070080, class: __NSCFConstantString

======copy对象=====
object_p: 0x610000267300, class: __NSCFString
object_p: 0x107070080, class: __NSCFConstantString

======mutableCopy对象=====
object_p: 0x610000267300, class: __NSCFString
object_p: 0x107070080, class: __NSCFConstantString


结论:
从上述输出中可以看出容器类可变对象mutableCopy和copy都返回一个新的容器,但容器内的元素仍然是浅拷贝

总结:

通过上述代码分析:

  1. copy: 对于可变对象为深拷贝,对于不可变对象为浅拷贝
  2. mutableCopy:始终是深拷贝

自定义类对象的深浅拷贝

在OC中不是所有的类都支持拷贝,只有遵循<NSCopying>才支持copy,只有遵循<NSMutableCopying>才支持mutableCopy。如果没有遵循,拷贝时会直接Crash。

代码示例:

#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCopying, NSMutableCopying>

@property (nonatomic, copy) NSString *name;

@end


#import "Person.h"

@implementation Person

- (id)copyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}

- (id)mutableCopyWithZone:(NSZone *)zone {
    Person *person = [Person allocWithZone:zone];
    person.name = self.name;
    return person;
}

@end

实现容器对象的完全拷贝

NSMutableString *str1 = [NSMutableString stringWithFormat:@"非容器可变对象"];
    NSArray *array = [NSArray arrayWithObjects:str1, @"非容器不可变对象", nil];
    
    NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:array copyItems:YES];
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:mutableArray];
    NSMutableArray *newMutableArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    
    NSLog(@"===原对象===");
    NSLog(@"array[0] : %p, array[1] : %p", array[0], array[1]);
    
    NSLog(@"===新对象===");
    NSLog(@"mutableArray[0] : %p, mutableArray[1] : %p", mutableArray[0], mutableArray[1]);
    
    NSLog(@"===完全拷贝对象===");
    NSLog(@"newMutableArray[0] : %p, newMutableArray[1] : %p", newMutableArray[0], newMutableArray[1]);

输出:

===原对象===
array[0] : 0x6180000697c0, array[1] : 0x10f40f098

===新对象===
mutableArray[0] : 0x618000055060, mutableArray[1] : 0x10f40f098

===完全拷贝对象===
newMutableArray[0] : 0x618000055600, newMutableArray[1] : 0x6180000556f0

如果是自定义类对象 需要遵循<NSCoding>否则在使用NSKeyedArchiver的时候会Crash

自定义对象代码如下

#import <Foundation/Foundation.h>

@interface Person : NSObject <NSCoding>

@property (nonatomic, copy) NSString *name;

@end


#import "Person.h"

@implementation Person

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self.name = [aDecoder decodeObjectForKey:@"name"];
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
}

@end

获取更佳阅读体验深刻理解iOS"深拷贝"与"浅拷贝"

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容