copy 是浅拷贝,mutableCopy 是深拷贝,从我学习的时候开始 老师貌似也是这么说的,我也一直是这么记的【尴尬】这是不对的!!!!!!!!!!
创建一个对象, 该对象与源对象的内容一致,此时使用copy与mutableCopy的区别:
copy拷贝出来的对象类型总是不可变类型(例如, NSString, NSDictionary, NSArray等等)
mutableCopy拷贝出来的对象类型总是可变类型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)
声明属性的时候修饰使用copy修饰的原因:
补充一点堆栈的介绍:
堆和栈是操作系统的内存中堆和栈,不是数据结构中的堆和栈。
1、堆(heap)区:堆是由程序员分配和释放,用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。当进程调用alloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用realse释放内存时,被释放的内存从堆中被剔除(堆被缩减),因为我们现在iOS基本都使用ARC来管理对象,所以不用我们程序员来管理,但是我们要知道这个对象存储的位置。
2、栈(stack)区:栈是由编译器自动分配并释放,用户存放程序临时创建的局部变量,存放函数的参数值,局部变量等。也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味这在数据段中存放变量)。除此以外在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也回被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上将我们可以把栈看成一个临时数据寄存、交换的内存区。
- block为什么要用copy?
MRC下不使用copy修饰, block存储在栈区, 作用域结束, block这个局部变量被销毁, 而一个strong指针还指向了这个被回收的地址.所以使用copy,把block拷贝到堆区, 并且指向堆区的block. 但是在ARC下无论copy,还是stong都无所谓, 因为系统已经在创建block的时候, 已经拷贝到堆区了。copy在MRC中是为了保护block的封装性将其移动至堆区。但在ARC中更多的是为了语义化、因为系统自动帮你移动了
补充个常用的NSString用copy修饰的原因:
用copy是为了安全,防止NSMutableString赋值给NSString时,前者修改引起后者值变化而用的.这个也适用于别的下文也会提到并验证。
@property (nonatomic,strong) NSString *myName;
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"daLi"];
self.myName = mStr;
NSLog(@"%@", self.myName);
[mStr appendString:@"好美"];
NSLog(@"%@", mStr);
NSLog(@"%@", self.myName);
/*
分析:self.myName声明的时候使用strong修饰,会跟着mStr的改变而改变
输出:
2017-12-11 16:41:39.146126+0800 Demo[20438:1163039] daLi
2017-12-11 16:41:39.146357+0800 Demo[20438:1163039] daLi好美
2017-12-11 16:41:39.146496+0800 Demo[20438:1163039] daLi好美
*/
@property (nonatomic,copy) NSString *myName;
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"daLi"];
self.myName = mStr;
NSLog(@"%@", self.myName);
[mStr appendString:@"好美"];
NSLog(@"%@", mStr);
NSLog(@"%@", self.myName);
/*
分析:self.myName声明的时候使用copy修饰,不会跟着mStr的改变而改变
输出:
2017-12-11 16:46:08.367018+0800 Demo[20507:1166962] daLi
2017-12-11 16:46:08.367227+0800 Demo[20507:1166962] daLi好美
2017-12-11 16:46:08.367353+0800 Demo[20507:1166962] daLi
*/
浅拷贝和深拷贝的分析
先介绍一下什么是iOS里面的系统容器类:
比如NSArray,NSMutableArray,NSDictionary,NSMutableDictionary....
浅拷贝和深拷贝?????英文就是shallow copy和deep copy
- 浅拷贝:正常的拷贝,生成一个新的容器,但却是和原来的容器共用内部的元素;
- 深拷贝:不仅生成新的容器,还生成了新的内部元素。
稍作总结:浅拷贝复制容器,深拷贝复制容器及其内部元素
(copy 与 mutableCopy 不等同于浅拷贝与深拷贝)
接下来用代码验证这些:
NSMutableArray *mArr = [NSMutableArray arrayWithObject:@1];
NSLog(@"1-%@--%p", mArr, mArr);
NSMutableArray *marr0 = [NSMutableArray arrayWithObject:mArr];
NSLog(@"2-%@--%p--%p", marr0, marr0, marr0[0]);
NSMutableArray *mCopyArr = [marr0 mutableCopy];//替换成copy是一样的效果
NSLog(@"3-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
[mCopyArr[0] addObject:@2];
NSLog(@"4-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
NSLog(@"5-%@--%p--%p", marr0, marr0, marr0[0]);
[marr0[0] addObject:@3];
NSLog(@"6-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
NSLog(@"7-%@--%p--%p", marr0, marr0, marr0[0]);
/*
修改了mCopyArr的元素,marr0也跟着改变了元素,因为二者的元素是共用的,[marr0 mutableCopy]只拷贝了数组容器,里面的元素并没有拷贝,而是和marr0共用的元素,所以marr0[0]、mCopyArr[0]元素的地址是一个,无论修改它们俩哪一个,对应的数组marr0、mCopyArr里面的元素都会跟着改变。
由此验证出来:容器类的mutableCopy和copy是浅拷贝,只拷贝容器,并不拷贝元素!!
输出结果:
2017-12-11 14:41:53.551658+0800 Demo[19619:1091522] 1-(
1
)--0x600000059f50
2017-12-11 14:41:53.551876+0800 Demo[19619:1091522] 2-(
(
1
)
)--0x6000002576a0--0x600000059f50
2017-12-11 14:41:53.552080+0800 Demo[19619:1091522] 3-(
(
1
)
)--0x60000000b9f0--0x600000059f50
2017-12-11 14:41:53.552357+0800 Demo[19619:1091522] 4-(
(
1,
2
)
)--0x60000000b9f0--0x600000059f50
2017-12-11 14:41:53.552545+0800 Demo[19619:1091522] 5-(
(
1,
2
)
)--0x6000002576a0--0x600000059f50
2017-12-11 14:41:53.552687+0800 Demo[19619:1091522] 6-(
(
1,
2,
3
)
)--0x60000000b9f0--0x600000059f50
2017-12-11 14:41:53.552806+0800 Demo[19619:1091522] 7-(
(
1,
2,
3
)
)--0x6000002576a0--0x600000059f50
*/
写一个NSArray验证一下
NSArray *arr = @[@"1"];
NSLog(@"1-%@--%p", arr, arr);
NSArray *arr0 = arr;
NSLog(@"2-%@--%p", arr0, arr0);
NSArray *arr1 = [arr copy];
NSLog(@"3-%@--%p", arr1, arr1);
NSArray *arr2 = [arr mutableCopy];
NSLog(@"4-%@--%p", arr2, arr2);
arr = @[@"2"];
NSLog(@"5-%@--%p", arr, arr);
NSLog(@"6-%@--%p", arr0, arr0);
NSLog(@"7-%@--%p", arr1, arr1);
NSLog(@"8-%@--%p", arr2, arr2);
/*
分析:
arr初始化指向的是@[@"1"]数组,
1、arr0是直接用arr赋值的,都是指向了@[@"1"],当arr = @[@"2"],arr指向了@[@"2"]这个数组,而arr0不变,因为arr0依旧指向的是@[@"1"];
2、arr1,[arr copy],使得arr1指向了arr指向的@[@"1"],后面arr的指向改变并不会导致arr1的指向改变;
3、arr2,[arr mutableCopy],使得arr1指向了arr指向的@[@"1"],后面arr的指向改变并不会导致arr1的指向改变;
输出:
2017-12-11 15:12:21.130514+0800 Demo[19697:1107933] 1-(
1
)--0x600000001c20
2017-12-11 15:12:21.130690+0800 Demo[19697:1107933] 2-(
1
)--0x600000001c20
2017-12-11 15:12:21.130835+0800 Demo[19697:1107933] 3-(
1
)--0x600000001c20
2017-12-11 15:12:21.130994+0800 Demo[19697:1107933] 4-(
1
)--0x604000056770
2017-12-11 15:12:21.131122+0800 Demo[19697:1107933] 5-(
2
)--0x604000002380
2017-12-11 15:12:21.131255+0800 Demo[19697:1107933] 6-(
1
)--0x600000001c20
2017-12-11 15:12:21.131342+0800 Demo[19697:1107933] 7-(
1
)--0x600000001c20
2017-12-11 15:12:21.131431+0800 Demo[19697:1107933] 8-(
1
)--0x604000056770
*/
cpoy 与直接赋值的对比分析
直接赋值:
NSMutableArray *mArr = [NSMutableArray arrayWithObjects:@"1", nil];
NSLog(@"0-%@--%p", mArr, mArr);
NSArray *arr = mArr;
NSLog(@"1-%@--%p", arr, arr);
[mArr addObject:@"2"];
NSLog(@"2-%@--%p", mArr, mArr);
NSLog(@"3-%@--%p", arr, arr);
/*
分析:使用直接赋值的方式给arr=mArr,可变数组mArr添加对象是在赋值之后, 为什么后面添加对象还会影响到不可变数组arr呢?? 原因很简单, 因为Objective-C支持多态. 所以表面上arr是NSArray对象, 其实骨子里是NSMutableArray对象.这样的话将会对后期DEBUG增加很大的成本, 可能会导致莫名其妙的错误.开发过程中应该注意此类情况
输出:
2017-12-11 15:46:21.532252+0800 Demo[20106:1129830] 0-(
1
)--0x604000441350
2017-12-11 15:46:21.532407+0800 Demo[20106:1129830] 1-(
1
)--0x604000441350
2017-12-11 15:46:21.532528+0800 Demo[20106:1129830] 2-(
1,
2
)--0x604000441350
2017-12-11 15:46:21.532652+0800 Demo[20106:1129830] 3-(
1,
2
)--0x604000441350
*/
copy/mutableCopy:
NSMutableArray *mArr = [NSMutableArray arrayWithObjects:@"1", nil];
NSLog(@"0-%@--%p", mArr, mArr);
NSArray *arr = [mArr copy];//mutableCopy
NSLog(@"1-%@--%p", arr, arr);
[mArr addObject:@"2"];
NSLog(@"2-%@--%p", mArr, mArr);
NSLog(@"3-%@--%p", arr, arr);
/*
分析:arr使用copy获取之后,再次修改mArr不会造成arr的改变,保障了arr的安全。
输出:
2017-12-11 15:51:29.749360+0800 Demo[20137:1133165] 0-(
1
)--0x60000005daf0
2017-12-11 15:51:29.749537+0800 Demo[20137:1133165] 1-(
1
)--0x600000007ec0
2017-12-11 15:51:29.749639+0800 Demo[20137:1133165] 2-(
1,
2
)--0x60000005daf0
2017-12-11 15:51:29.749787+0800 Demo[20137:1133165] 3-(
1
)--0x600000007ec0
*/
然后再写一个字典的来验证验证:
NSDictionary *dictA = @{@"a": @"a"};
NSDictionary *dictB = dictA;
dictA = @{@"b": @"b"};
NSLog(@"%@ - %p",dictA, dictA); // {b = b}
NSLog(@"%@ - %p",dictB, dictB); // {a = a}
/*
dictB = dictA, 代表dictA和dictB都指向{@"a" : @"a"},
dictA = @{@"b" : @"b"}这行代码之后dictA就转而指向@{@"b" : @"b"}了.
而dictA仍然指向{@"a" : @"a"}.
所以输出:
{
b = b;
} - 0x604000039ae0
{
a = a;
} - 0x604000030180
*/
Person *pA = [[Person alloc] init];
pA.name = @"A1";
Person *pB = [[Person alloc] init];
pB = pA;
pA.name = @"A2";
NSLog(@"%@ - %p - %p", pA.name, pA, &pA);
NSLog(@"%@ - %p - %p", pB.name, pB, &pB);
/*
Person *pB = [[Person alloc] init];这句代码是没有任何作用的, 对pB后面的指向没有任何影响, pB = pA这句代码过后, pB和pA指向的是同一个对象, 那么通过pA修改其指向的对象的name属性, 自然也会影响到pB指向的对象的name属性. 因为他们两个指向的是同一个对象
A2 - 0x600000017a50 - 0x7fff5c47cb40
A2 - 0x600000017a50 - 0x7fff5c47cb38
*/