Shallow Copy & Deep Copy

对象的拷贝需要首先实现<NSCoping><NSMutableCopying>协议

浅拷贝:object Aobject B及其属性使用同样的内存地址,简单说明的话,可以认为是指针复制;
深拷贝:object Aobject B及其属性使用不同的内存地址,简单说明的话,可以认为是内容复制。

自定义对象的拷贝

CommonClass

// CommonClass.h
@interface CommonClass : NSObject<NSCopying>
@property (nonatomic, copy) NSString *msg;
@end
// CommonClass.m
@implementation CommonClass
- (instancetype)init{
    self = [super init];
    if (self) {
        self.msg = @"CommonClass";
    }
    return self;
}
- (id)copyWithZone:(NSZone *)zone{
    CommonClass *aObj = [[[self class] allocWithZone:zone] init];
    aObj.msg = self.msg;
    return aObj;
}

@end

接下来做copy测试

void copy(void) {
    CommonClass *obj1 = [[CommonClass alloc] init];
    NSLog(@"obj1 %p, msg:%p", obj1, obj1.msg);
    >>> obj1 0x105904360, msg:0x10000ce58

    CommonClass *obj2 = [obj1 copy];
    NSLog(@"obj2 %p, msg:%p", obj2, obj2.msg);
    >>> obj2 0x105906080, msg:0x10000ce58
}

结论

  • 自定义对象的copymsg进行了浅拷贝

自定义对象的嵌套

CommonCopyClass

// CommonCopyClass.h
@interface CommonCopyClass : NSObject<NSCopying>
@property (nonatomic, strong) CommonClass *objA;
@property (nonatomic, copy) NSString *msg;
@end
// CommonCopyClass.m
@implementation CommonCopyClass

- (instancetype)init{
    self = [super init];
    if (self) {
        self.objA = [[CommonClass alloc] init];
        self.msg = @"msg";
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone{
    CommonCopyClass *aObj = [[[self class] allocWithZone:zone] init];
    aObj.objA = [self.objA copy];
    aObj.msg = self.msg;
    return aObj;
}

@end

CommonCopy2Class

// CommonCopy2Class.h
@interface CommonCopy2Class : NSObject<NSCopying>
@property (nonatomic, strong) CommonCopyClass *objCopyA;
@end
// CommonCopy2Class.m
@implementation CommonCopy2Class

- (instancetype)init{
    self = [super init];
    if (self) {
        self.objCopyA = [[CommonCopyClass alloc] init];
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone{
    CommonCopy2Class *aObj = [[[self class] allocWithZone:zone] init];
    aObj.objCopyA = [self.objCopyA copy];
    return aObj;
}

@end

copy测试

    CommonCopyClass *obj4 = [[CommonCopyClass alloc] init];
    NSLog(@"obj4 %p, param:%p, subMsg:%p", obj4, obj4.objA, obj4.objA.msg);
        >>> obj4 0x105906610, param:0x105906570, subMsg:0x10000ce58

    CommonCopyClass *obj5 = [obj4 copy];
    NSLog(@"obj5 %p, param:%p, subMsg:%p", obj5, obj5.objA, obj5.objA.msg);
        >>> obj5 0x105804160, param:0x105804290, subMsg:0x10000ce58    

    CommonCopy2Class *obj7 = [[CommonCopy2Class alloc] init];
    NSLog(@"obj7 %p, param:%p, subParam:%p, subsubMsg:%p", obj7, obj7.objCopyA, obj7.objCopyA.objA, obj7.objCopyA.objA.msg);
        >>> obj7 0x100607fd0, param:0x1058040d0, subParam:0x105804180, subsubMsg:0x10000ce58

    CommonCopy2Class *obj8 = [obj7 copy];
    NSLog(@"obj8 %p, param:%p, subParam:%p, subsubMsg:%p", obj8, obj8.objCopyA, obj8.objCopyA.objA, obj8.objCopyA.objA.msg);
        >>> obj8 0x1058040c0, param:0x1058044b0, subParam:0x1058044e0, subsubMsg:0x10000ce58

结论

  • 自定义对象的copymutableCopy方法,多层嵌套的子类进行了深拷贝,多层嵌套子类的NSString属性msg进行了copymutableCopy

容器类的拷贝

NSArray的拷贝

void arrayCopy(void) {
    CommonCopy2Class *obj1 = [[CommonCopy2Class alloc] init];
    CommonCopy2Class *obj2 = [[CommonCopy2Class alloc] init];
    NSLog(@"obj1 %p, obj2 %p", obj1, obj2);
        >>> obj1 0x105c040c0, obj2 0x105c04230
  
    NSArray *a1 = @[obj1, obj2];
    NSLog(@"a1 %@, %p, item:%@",NSStringFromClass([a1 class]), a1, a1);
    >>> a1 __NSArrayI, 0x10062fcd0, "<CommonCopy2Class: 0x105c040c0>", "<CommonCopy2Class: 0x105c04230>"
      
    NSArray *a2 = [a1 copy];
    NSLog(@"a2 %@, %p, item:%@",NSStringFromClass([a2 class]), a2, a2);
    >>> a2 __NSArrayI, 0x10062fcd0, "<CommonCopy2Class: 0x105c040c0>", "<CommonCopy2Class: 0x105c04230>"
    
    NSArray *a3 = [a1 mutableCopy];
    NSLog(@"a3 %@, %p, item:%@",NSStringFromClass([a3 class]), a3, a3);
    >>> a3 __NSArrayM, 0x100507500, "<CommonCopy2Class: 0x105c040c0>", "<CommonCopy2Class: 0x105c04230>"
      
    NSMutableArray *ma1 = [[NSMutableArray alloc] initWithArray:a1];
    NSLog(@"ma1 %@, %p, item:%@",NSStringFromClass([ma1 class]), ma1, ma1);
        >>> ma1 __NSArrayM, 0x10125ba70, "<CommonCopy2Class: 0x105c040c0>", "<CommonCopy2Class: 0x105c04230>"
      
    NSMutableArray *ma2 = [ma1 copy];
    NSLog(@"ma2 %@, %p, item:%@",NSStringFromClass([ma2 class]), ma2, ma2);
        >>> ma2 __NSArrayI, 0x100606940, "<CommonCopy2Class: 0x105c040c0>", "<CommonCopy2Class: 0x105c04230>"   
  
    NSMutableArray *ma3 = [ma1 mutableCopy];
    NSLog(@"ma3 %@, %p, item:%@",NSStringFromClass([ma3 class]), ma3, ma3); 
    >>> ma3 __NSArrayM, 0x100708fe0, "<CommonCopy2Class: 0x105c040c0>", "<CommonCopy2Class: 0x105c04230>"
}

结论

  • 无论是NSCopy或是NSMutableCopy,容器内的CommonCopy2Class对象,都没有进行拷贝
  • CommonCopy2ClasscopyWithZone没有执行
  • NSArray
    • NSArray对象的copy方法,返回__NSArrayI对象,浅拷贝
    • NSArray对象的NSMutableCopy方法,返回__NSArrayM对象,是一个可变数组。对数组进行了第一层的深拷贝
  • NSMutableArray
    • NSMutableArray对象的copy方法,返回__NSArrayI对象,不再可变,对原有数组进行了第一层深拷贝
    • NSMutableArray对象的NSMutableCopy方法,返回__NSArrayM对象,是一个可变数组,对原有数组进行了第一层深拷贝

NSDictionary

void dictionaryCopy(void) {
    CommonCopy2Class *obj1 = [[CommonCopy2Class alloc] init];
    CommonCopy2Class *obj2 = [[CommonCopy2Class alloc] init];
    NSLog(@"obj1 %p, obj2 %p", obj1, obj2);
        >>> obj1 0x101916b70, obj2 0x101904160
    
    NSDictionary *d1 = @{@"1": obj1, @"2": obj2};
    NSLog(@"d1 %@, %p, item:%@",NSStringFromClass([d1 class]), d1, d1);
    >>> d1 __NSDictionaryI, 0x101c09b70, "<CommonCopy2Class: 0x101916b70>", "<CommonCopy2Class: 0x101904160>"
  
    NSDictionary *d2 = [d1 copy];
    NSLog(@"d2 %@, %p, item:%@",NSStringFromClass([d2 class]), d2, d2);
    >>> d2 __NSDictionaryI, 0x101c09b70, "<CommonCopy2Class: 0x101916b70>", "<CommonCopy2Class: 0x101904160>" 
  
    NSDictionary *d3 = [d1 mutableCopy];
    NSLog(@"d3 %@, %p, item:%@",NSStringFromClass([d3 class]), d3, d3);
    >>> d3 __NSDictionaryM, 0x1019172e0, "<CommonCopy2Class: 0x101916b70>", "<CommonCopy2Class: 0x101904160>"
    
  
    NSMutableDictionary *md1 = [[NSMutableDictionary alloc] initWithDictionary:d1];
    NSLog(@"md1 %@, %p, item:%@",NSStringFromClass([md1 class]), md1, md1);
        >>> md1 __NSDictionaryM, 0x101840690, "<CommonCopy2Class: 0x101916b70>", "<CommonCopy2Class: 0x101904160>"
  
    NSMutableDictionary *md2 = [md1 copy];
    NSLog(@"md2 %@, %p, item:%@",NSStringFromClass([md2 class]), md2, md2);
        >>> md2 __NSFrozenDictionaryM, 0x101843ba0, "<CommonCopy2Class: 0x101916b70>", "<CommonCopy2Class: 0x101904160>"
  
    NSMutableDictionary *md3 = [md1 mutableCopy];
    NSLog(@"md3 %@, %p, item:%@",NSStringFromClass([md3 class]), md3, md3);
    >>> md3 __NSDictionaryM, 0x1006068e0, "<CommonCopy2Class: 0x101916b70>", "<CommonCopy2Class: 0x101904160>"
}

结论

  • 无论是NSCopy或是NSMutableCopy,容器内的CommonCopy2Class对象,都没有进行拷贝
  • CommonCopy2ClasscopyWithZone没有执行
  • NSDictionary
    • NSDictionary对象的copy方法,返回__NSDictionaryM对象,浅拷贝
    • NSDictionary对象的NSMutableCopy方法,返回__NSDictionaryM对象,是一个可变字典,第一层深拷贝
  • NSMutableDictionary
    • NSMutableDictionary对象的copy方法,返回__NSFrozenDictionaryM对象。虽然是M结尾,但他并不是一个可变的字典类。对原有的字典进行了第一层深拷贝
    • NSMutableDictionary对象的NSMutableCopy方法,返回__NSDictionaryM对象,是一个可变数组,对原有字典进行了第一层深拷贝

copyItems

void initCopy(void) {
    CommonCopy2Class *obj1 = [[CommonCopy2Class alloc] init];
    CommonCopy2Class *obj2 = [[CommonCopy2Class alloc] init];
    NSLog(@"obj1 %p, obj2 %p", obj1, obj2);
    >>> obj1 0x1005089c0, obj2 0x10053bf80
    NSLog(@"obj1 %p, param:%p, subParam:%p, subsubParam:%p", obj1, obj1.objCopyA, obj1.objCopyA.objA, obj1.objCopyA.objA.msg);      // 输出1
    >>> obj1 0x1005089c0, param:0x100546490, subParam:0x10053d870, subsubMsg:0x10000ceb8

    NSArray *a1 = @[obj1, obj2];
    NSLog(@"a1 %@, %p, item:%@",NSStringFromClass([a1 class]), a1, a1);
    >>> a1 __NSArrayI, 0x100546af0, "<CommonCopy2Class: 0x1005089c0>", "<CommonCopy2Class: 0x10053bf80>"
      
    NSArray *a2 = [[NSArray alloc] initWithArray:a1 copyItems:YES];
    NSLog(@"a2 %@, %p, item:%@",NSStringFromClass([a2 class]), a2, a2);
    >>> a2 __NSArrayI, 0x100546b90, "<CommonCopy2Class: 0x100546a20>", "<CommonCopy2Class: 0x100546890>"
  
    CommonCopy2Class *obj3 = a2.firstObject;
    NSLog(@"obj3 %p, param:%p, subParam:%p, subsubParam:%p", obj3, obj3.objCopyA, obj3.objCopyA.objA, obj3.objCopyA.objA.msg);      // 输出2
    >>> obj3 0x100546a20, param:0x100546ef0, subParam:0x10053ce40, subsubMsg:0x10000ceb8
    
    NSMutableArray *ma1 = [[NSMutableArray alloc] initWithArray:a1 copyItems:YES];
    NSLog(@"ma1 %@, %p, item:%@",NSStringFromClass([ma1 class]), ma1, ma1);
    >>> ma1 __NSArrayM, 0x106104480, "<CommonCopy2Class: 0x106305150>", "<CommonCopy2Class: 0x106305160>"
  
    CommonCopy2Class *obj4 = ma1.firstObject;
    NSLog(@"obj4 %p, param:%p, subParam:%p, subsubParam:%p", obj4, obj4.objCopyA, obj4.objCopyA.objA, obj4.objCopyA.objA.msg);      // 输出3
        >>> obj4 0x106104340, param:0x106104380, subParam:0x1061043b0, subsubMsg:0x10000ceb8


    NSDictionary *d1 = @{@"1": obj1, @"2": obj2};
    NSLog(@"d1 %@, %p, item:%@",NSStringFromClass([d1 class]), d1, d1);
    >>> d1 __NSDictionaryI, 0x100704450, 1 = "<CommonCopy2Class: 0x1005089c0>"; 2 = "<CommonCopy2Class: 0x10053bf80>";
  
    NSDictionary *d2 = [[NSDictionary alloc] initWithDictionary:d1 copyItems:YES];
    NSLog(@"d2 %@, %p, item:%@",NSStringFromClass([d2 class]), d2, d2);
    >>> d2 __NSDictionaryI, 0x1063050b0, 1 = "<CommonCopy2Class: 0x106304e00>"; 2 = "<CommonCopy2Class: 0x106304e30>";
  
    CommonCopy2Class *obj5 = d2[@"1"];
    NSLog(@"obj5 %p, param:%p, subParam:%p, subsubParam:%p", obj5, obj5.objCopyA, obj5.objCopyA.objA, obj5.objCopyA.objA.msg);
    >>> obj5 0x106304e00, param:0x106304e40, subParam:0x106305190, subsubMsg:0x10000ceb8
    
    NSMutableDictionary *md1 = [[NSMutableDictionary alloc] initWithDictionary:d1 copyItems:YES];
    NSLog(@"md1 %@, %p, item:%@",NSStringFromClass([md1 class]), md1, md1);
    >>> md1 __NSDictionaryM, 0x106504090, 1 = "<CommonCopy2Class: 0x106504080>"; 2 = "<CommonCopy2Class: 0x1065040b0>";
  
    CommonCopy2Class *obj6 = md1[@"1"];
    NSLog(@"obj6 %p, param:%p, subParam:%p, subsubParam:%p", obj6, obj6.objCopyA, obj6.objCopyA.objA, obj6.objCopyA.objA.msg);
    >>> obj6 0x106504080, param:0x1065040c0, subParam:0x1065040f0, subsubMsg:0x10000ceb8
}

结论

  • copyItems会对容器内部的元素进行copy操作,即对元素进行了深拷贝。

容器的嵌套

void nestedCopy (void) {
    CommonCopy2Class *obj1 = [[CommonCopy2Class alloc] init];
    CommonCopy2Class *obj2 = [[CommonCopy2Class alloc] init];
    CommonCopy2Class *obj3 = [[CommonCopy2Class alloc] init];
    NSLog(@"obj1 %p, obj2 %p ,obj3 %p", obj1, obj2, obj3);              // 输出1      
    >>> obj1 0x105a04100, obj2 0x105a04560 ,obj3 0x105a045a0
    
    NSArray *oa1 = @[obj1, obj2];
    NSArray *oa2 = @[obj3];
    NSLog(@"oa1 %p, oa2 %p", oa1, oa2);                                                     // 输出2
        >>> oa1 0x105a04710, oa2 0x105a04730
    
    NSArray *a1 = @[oa1, oa2];
    NSLog(@"a1 %p", a1);
        >>> a1 0x105a045e0
      
    NSArray *a2 = [a1 copy];
    NSLog(@"a2 %p", a2);                                                                                    // 输出3
    >>> a2 0x105a045e0
     
    NSArray *a3 = [[NSArray alloc] initWithArray:a1 copyItems:YES];
    NSLog(@"a3 %p", a3);
    >>> a3 0x105a04600
      
    NSLog(@"oa1 %p, oa2 %p", a3.firstObject, a3.lastObject);            // 输出6
    >>> oa1 0x105a04710, oa2 0x105a04730
      
    NSMutableArray *ma1 = [[NSMutableArray alloc] initWithArray:a1];
    NSLog(@"oa1 %p, oa2 %p", ma1.firstObject, ma1.lastObject);
        >>> oa1 0x105a04710, oa2 0x105a04730
  
    NSMutableArray *ma2 = [[NSMutableArray alloc] initWithArray:ma1 copyItems:YES];
    NSLog(@"oa1 %p, oa2 %p", ma2.firstObject, ma2.lastObject);      // 输出7
        >>> oa1 0x105a04710, oa2 0x105a04730
    CommonCopy2Class *obj6 = ((NSArray *)ma2.firstObject).firstObject;
    NSLog(@"obj6 %p, param:%p, subParam:%p, subsubParam:%p", obj6, obj6.objCopyA, obj6.objCopyA.objA, obj6.objCopyA.objA.msg);
    >>> obj6 0x105a04100, param:0x105a04190, subParam:0x105a04490, subsubParam:0x10000ceb8
}

结论

  • 如果NSArray的元素是NSArray类型(这里是和NSMutableArray做区分,可能是__NSArrayI类型)subArray是同一个

补充

void nestedCopy2 (void) {
    CommonCopy2Class *obj1 = [[CommonCopy2Class alloc] init];
    CommonCopy2Class *obj2 = [[CommonCopy2Class alloc] init];
    CommonCopy2Class *obj3 = [[CommonCopy2Class alloc] init];
    NSLog(@"obj1 %p, obj2 %p ,obj3 %p", obj1, obj2, obj3);
    
    NSMutableArray *ma1 = [[NSMutableArray alloc] initWithObjects:obj1, obj2, nil];
    NSMutableArray *ma2 = [[NSMutableArray alloc] initWithObjects:obj3, nil];
    
    NSArray *sa1 = [[NSArray alloc] initWithObjects:ma1, ma2, nil];
    NSLog(@"%@ %p", sa1, sa1.firstObject);
    
    NSArray *sa2 = [[NSArray alloc] initWithArray:sa1 copyItems:YES];
    NSLog(@"%@ %p", sa2, sa2.firstObject);
}
(
        (
        "<CommonCopy2Class: 0x100743f10>",
        "<CommonCopy2Class: 0x100741220>"
    ),
        (
        "<CommonCopy2Class: 0x10073f780>"
    )
) 0x100746c70
(
        (
        "<CommonCopy2Class: 0x100743f10>",
        "<CommonCopy2Class: 0x100741220>"
    ),
        (
        "<CommonCopy2Class: 0x10073f780>"
    )
) 0x100746f30
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容