iOS开发中,对象的拷贝有两种方法,就是江湖中流传的『浅拷贝』与『深拷贝』。浅拷贝意即不拷贝内容的本身,只对其指针进行复制,而深拷贝就是对内容的拷贝了,即是把内容拷贝到一块新的内存中去。apple iOS官方文档中有个图,一看就明白是咋回事了,如下图


简单来说,浅拷贝就是复制指针,深拷贝就是复制内容。


1、集合的深浅拷贝

NSArray *contentArray = @[@"鼠",@"牛",@"虎",@"兔",@"龙",@"蛇",@"马"];
    NSSet *contentSet = [[NSSet alloc] initWithObjects:@"羊",@"猴",@"鸡",@"狗",@"猪", nil];
    NSDictionary *contentDic = @{@"shu" : @"鼠",
                                 @"niu" : @"牛",
                                 @"hu" : @"虎",
                                 @"tu" : @"兔",
                                 @"long" : @"龙"};
    /* 浅复制 */
    NSArray *shallowCopyArray = [contentArray copyWithZone:nil];
    NSArray *shallowCopyArray2 = [[NSArray alloc] initWithArray:contentArray copyItems:NO];
    NSSet *shallowCopySet = [contentSet copyWithZone:nil];
    NSSet *shallowCopySet2 = [[NSSet alloc] initWithSet:contentSet copyItems:NO];
    NSDictionary *shallowCopyDic = [contentDic copyWithZone:nil];
    NSDictionary *shallowCopyDic2 = [[NSDictionary alloc] initWithDictionary:contentDic copyItems:NO];
    /* 深复制 */
    NSArray *deepCopyArray = [[NSArray alloc] initWithArray:contentArray copyItems:YES];
    NSSet *deepCopySet = [[NSSet alloc] initWithSet:contentSet copyItems:YES];
    NSDictionary *deepCopyDic = [[NSDictionary alloc] initWithDictionary:contentDic copyItems:YES];
    
    //一组数据可观察出深浅拷贝
    NSLog(@"contentArray is %p,pointer is %p",contentArray,&contentArray);
    NSLog(@"shallowCopyArray is %p,pointer is %p",shallowCopyArray,&shallowCopyArray);
    NSLog(@"shallowCopyArray2 is %p,pointer is %p",shallowCopyArray2,&shallowCopyArray2);
    NSLog(@"deepCopyArray is %p,pointer is %p",deepCopyArray,&deepCopyArray);
    
    /*打印
     contentArray is 0x604000289b00,pointer is 0x7ffee0d8c7c8
     shallowCopyArray is 0x604000289b00,pointer is 0x7ffee0d8c7b0
     shallowCopyArray2 is 0x604000289b00,pointer is 0x7ffee0d8c7a8
     deepCopyArray is 0x60400028a000,pointer is 0x7ffee0d8c780
     */
复制代码

由打印的结果,可以证明浅拷贝与深拷贝的区别了。

2、集合/非集合类型的copy与mutableCopy

除了上面的方法,拷贝对象时还可使用copy、mutableCopy,具体使用如下:

NSArray *imutableArray = @[@"a",@"b",@"c"];
    NSArray *mutableArray = [NSMutableArray arrayWithObjects:@"d",@"e",@"f",nil];
    NSString *imutableString = @"imutableString";
    NSMutableString *mutableString = [NSMutableString stringWithString:imutableString];
    //
    //imutable对象 copy与mutableCopy
    NSArray *imutable_copyArray = [imutableArray copy]; 
    NSMutableArray *imutable_mutableCopyArray = [imutableArray mutableCopy];
    //[imutable_mutableCopyArray addObject:@"something2"];
    NSLog(@"———————————————————————————集合类型的copy star——————————————————————————————");
    NSLog(@"imutableArray is %p,pointer is %p",imutableArray,&imutableArray);
    NSLog(@"imutable_copyArray is %p,pointer is %p",imutable_copyArray,&imutable_copyArray);
    NSLog(@"imutable_mutableCopyArray is %p,pointer is %p",imutable_mutableCopyArray,&imutable_mutableCopyArray);
    NSLog(@"———————————————————————————end——————————————————————————————");
    //
    NSString *imutable_copyString = [imutableString copy]; 
    NSMutableString *imutable_mutableCopyString = [imutableString mutableCopy];
    [imutable_mutableCopyString appendString:@"someString2"];
    NSLog(@"———————————————————————————非集合类型的copy star——————————————————————————————");
    NSLog(@"imutableString is %p,pointer is %p",imutableString,&imutableString);
    NSLog(@"imutable_copyString is %p,pointer is %p",imutable_copyString,&imutable_copyString);
    NSLog(@"imutable_mutableCopyString is %p,pointer is %p",imutable_mutableCopyString,&imutable_mutableCopyString);
    NSLog(@"———————————————————————————end——————————————————————————————");
    
    //mutable对象 copy与mutableCopy
    NSMutableArray *mutable_copyArray = [mutableArray copy];
    // [mutable_copyArray addObject:@"otherthing"];//runtime crash
    NSMutableArray *mutable_mutableCopyArray = [mutableArray mutableCopy];
    //[mutable_mutableCopyArray addObject:@"otherthing2"];
    NSLog(@"———————————————————————————集合类型的mutableCopy star——————————————————————————————");
    NSLog(@"mutableArray is %p,pointer is %p",mutableArray,&mutableArray);
    NSLog(@"mutable_copyArray is %p,pointer is %p",mutable_copyArray,&mutable_copyArray);
    NSLog(@"mutable_mutableCopyArray is %p,pointer is %p",mutable_mutableCopyArray,&mutable_mutableCopyArray);
    NSLog(@"———————————————————————————end——————————————————————————————");
    //
    NSMutableString *mutable_copyString = [mutableString copy];
    //[mutable_copyString appendString:@"otherString"];//runtime crash
    NSMutableString *mutable_mutableCopyString = [mutableString mutableCopy];
    [mutable_mutableCopyString appendString:@"otherString2"];
    NSLog(@"———————————————————————————非集合类型的mutableCopy star——————————————————————————————");
    NSLog(@"mutableString is %p,pointer is %p",mutableString,&mutableString);
    NSLog(@"mutable_copyString is %p,pointer is %p",mutable_copyString,&mutable_copyString);
    NSLog(@"mutable_mutableCopyString is %p,pointer is %p",mutable_mutableCopyString,&mutable_mutableCopyString);
    NSLog(@"———————————————————————————end——————————————————————————————");
复制代码

上面代码中有2个runtime crash,分别是

NSMutableArray *mutable_copyArray = [mutableArray copy];
    // [mutable_copyArray addObject:@"otherthing"];//runtime crash
    以及  
    NSMutableString *mutable_copyString = [mutableString copy];
    //[mutable_copyString appendString:@"otherString"];//runtime crash
复制代码

这里要注意的一个点是,对象在使用copy方法之后,返回来的对象类型是不可变类型的(imutable),尽管你用一个可变类型的对象去接收,然后使用可变类型对象的方法,在运行时会crash,这是因为它终究是一个不可变(imutable)对象,由此可以得出第一个结论:

  • 不管集合、非集合类型对象使用copy返回来的为不可变类型对象,如果对copy返回对象使用mutable对象的接口就会产生runtime crash,而对象使用mutableCopy返回来的则是不可变类型对象。

好,注册crash代码后,再看看这段代码的最终打印结果:


由打印结果,可以得出第二个结论:

  • 对于不可变对象(imutable)使用copy为拷贝指针即浅拷贝,使用mutableCopy为内容拷贝;对于可变对象(mutable)使用copy、mutableCopy都为内容拷贝。

3、集合的深拷贝要注意的地方

先看一段代码,及其运行的结果:

NSLog(@"———————————————————————————imutable集合类型mutableCopy——————————————————————————————");
    NSLog(@"imutableArray pointer:");
    for (NSString *obj in imutableArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
    NSLog(@"imutable_mutableCopyArray pointer:");
    for (NSString *obj in imutable_mutableCopyArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }

    NSLog(@"———————————————————————————mutable集合类型copy与mutableCopy——————————————————————————————");
    NSLog(@"mutableArray pointer:");
    for (NSString *obj in mutableArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
    NSLog(@"mutable_copyArray pointer:");
    for (NSString *obj in mutable_copyArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
    NSLog(@"mutable_mutableCopyArray pointer:");
    for (NSString *obj in mutable_mutableCopyArray) {
        NSLog(@"obj is %p,pointer is %p",obj,&obj);
    }
复制代码

打印出来的结果:


这些集合虽然进行了深拷贝,但是其集合内的元素的内容不变,拷贝的只是元素的指针,所以这并不是完全意义的拷贝。apple官方称之为『one-level-deep copy』意思是单层的深拷贝,『not real-deep copy』不是真正意义上的深拷贝。

so,第三个结论:

  • 对于可变集合(mutable集合)使用copy/mutableCopy虽说是内容拷贝,但只止于对象,集合元素还是指针拷贝,所以这是单层的深拷贝,并不是完全拷贝。