深浅拷贝

实例化的对象存储在堆区,而指向对象的指针一般存储在栈区。实际上拷贝分为深拷贝(one level deep copy),浅拷贝(shallow copy)和完全拷贝(real deep copy)三种。

  • 浅拷贝:在操作中,对于被复制对象的每一层都是指针复制。


    深浅拷贝_第1张图片
  • 深拷贝:在操作中,对于被复制对象,至少有一层是深复制。


    深浅拷贝_第2张图片
  • 完全拷贝:在操作中,对于被复制对象,每一层都是对象复制。
深浅拷贝_第3张图片
image.png

注意

1、深拷贝和完全拷贝

深拷贝,就是把原有对象内容直接克隆一份新的对象,但是这里有一个坑,就是深拷贝只是复制一层对象,而不是复制第二层或者更深层的对象,例如:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableString *strM1 = [NSMutableString stringWithString:@"1"];
    NSMutableString *strM2 = [NSMutableString stringWithString:@"2"];
    NSMutableArray *arrM1 = [NSMutableArray arrayWithObjects: strM1, strM2, nil];

   // 深拷贝原数组
    NSMutableArray *arrM2 = [arrM1 mutableCopy];

   // 深拷贝之后,再修改字符“1“ 为 ”11“
    NSMutableString *strM3 = [arrM1 objectAtIndex:0];
    [strM3 appendString:@"1"];

    NSLog(@"strM1--%@",arrM1);
    NSLog(@"strM2--%@",arrM2);
}

看结果:

// 原数组
2017-03-14 00:22:02.260 [5522:393850] strM1--(
    11,
    2
)
// 深拷贝数组
2017-03-14 00:22:02.261 [5522:393850] strM2--(
    11,
    2
)

结论:为什么深拷贝已经复制了对象,那么原对象为什么也同步变化?这里就是深拷贝和完全拷贝的原因,深拷贝只是拷贝了一层数组,但是里面的字符串没有拷贝,两个数组都是用的同一个地址的字符串,所以改变一个,原对象也发生了变化。可以做下面这样的修改。

// copyIitems为YES
NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray: arrM1 copyItems:YES];

查看结果

2017-03-14 00:45:45.389 [5916:425450] arrM1--(
    11,
    2
)
2017-03-14 00:45:45.390 [5916:425450] arrM2--(
    1,
    2
)

可以利用这个方法,得到的是多一层的深复制,里面的字符串地址也进行了复制,所以改变strM3的值,不影响arrM1的值。你认为这样就解决了吗?再看下面的问题。

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableString *strM1 = [NSMutableString stringWithString:@"1"];
    NSMutableString *strM2 = [NSMutableString stringWithString:@"2"];
    NSMutableArray *arrM1 = [NSMutableArray arrayWithObjects: strM1, strM2, nil];

    // 嵌套了一层数组,arrM1
    NSMutableArray *arrM2 = [NSMutableArray arrayWithObjects: strM1, strM2, arrM1, nil];

    // copyItems会多拷贝一层
    NSMutableArray *arrM3 = [[NSMutableArray alloc] initWithArray:arrM2 copyItems:YES];
     
    // 两层深拷贝后再修改字符串
    NSMutableString *strM3 = [arrM1 objectAtIndex:0];
    [strM3 appendString:@"1"];

    NSLog(@"arrM2--%@",arrM2);
    NSLog(@"arrM3--%@",arrM3);
}

查看结果

2017-03-14 00:55:57.604 [6080:438490] arrM2--(
    11,
    2,
        (
        11,
        2
    )
)
2017-03-14 00:55:57.606 [6080:438490] arrM3--(
   // 第二层没有变化
    1,
    2,
        (
        // 第三层的字符串同步变化了。因为copyItem只是多拷贝了一层
        11,
        2
    )
)

结论:可以发现外层的深复制了,原对象和拷贝后的对象不是同一地址,再往里看一层都变化了,就没有深复制,也就是说再增加一层NSMutableArray *arrM2 = [[NSMutableArray alloc] initWithArray:arrM1 copyItems:YES];这个方法管不了那么多层数。可以采用归档和解档解决该问题:

NSMutableArray *arrM3 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:arrM2]];

查看结果

2017-03-14 01:17:23.204 [6396:464958] arrM2--(
    11,
    2,
        (
        11,
        2
    )
)
2017-03-14 01:17:23.204 [6396:464958] arrM3--(
    1,
    2,
        (
        1,
        2
    )
)

结论:可以看到实现了完全复制,就没有层数的限制


2、NSString的copy和strong

NSString被copy和strong修饰会有什么不同:

#import "ViewController.h"

@interface ViewController ()

@property (nonatomic,copy) NSString *str;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableString *strM = [[NSMutableString alloc] initWithString:@"bestDay"];
    self.str = strM;
    [strM appendString:@"OfThisYear"];
    NSLog(@"str----%@---%p",self.str,self.str);
    NSLog(@"strM----%@---%p",strM,strM);

}
@end

直接查看结果

2017-03-14 08:53:01.874 [789:18128] str----bestDay---0xa796144747365627
2017-03-14 08:53:01.875 [789:18128] strM----bestDayOfThisYear---0x600000071280

结论:可以看到copy修饰的str,在赋值以后,可变字符串strM发生了变化并不会影响str的值。从打印结果来看是因为二者不是一个地址,所以不会相互影响。为什么?
是因为copy修饰的属性setter方法,先release旧值,copy新值再赋值给成员变量,不可变copy是深拷贝,就是内容拷贝,地址变化了
接着看strong修饰的情况:

@property (nonatomic,strong) NSString *str;

直接查看结果

2017-03-14 09:03:36.756 [968:29890] str----bestDayOfThisYear---0x6000002600c0
2017-03-14 09:03:36.757 [968:29890] strM----bestDayOfThisYear---0x6000002600c0

结论:被strong修饰以后只是强指针引用,并未改变地址,所以str的值会随着strM进行变化,二者的地址也是相同的。


3、理解copy和retain的不同

在MRC下进行测试,先看代码

//copy和retain的区别
- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"111",@"222",@"333", nil];

    NSMutableArray *arrMRetain = [arrM retain];
    NSMutableArray *arrMCopy = [arrM copy];
    [arrM removeLastObject];

    NSLog(@"arrMRetain--%@--%p--%lu",arrMRetain,arrMRetain,[arrMRetain retainCount]);
    NSLog(@"arrMCopy--%@--%p--%lu",arrMCopy,arrMCopy,[arrMCopy retainCount]);

}

查看结

2017-03-14 20:16:59.895 copy和retain的区别[2816:177901] arrMRetain--(
    111,
    222
)--0x60000005cf50--2
2017-03-14 20:16:59.895 [2816:177901] arrMCopy--(
    111,
    222,
    333
)--0x60000005cf80--1

结论:copy是深复制,retainCount为1,retain是浅复制,retain是使原来对象引用计数加1,所以arrM和arrMRetain是同一地址,所以remove最后一个元素,arrMRetain也跟着变化了。

参考

ios中的拷贝你知道多少?

你可能感兴趣的:(深浅拷贝)