copy,strong,retain,weak和assign的区别

前言:

 

在初学iOS的时候,对于用什么关键词去修饰property,知其然而不知其所以然,大家都这么用,就这么用,不知道其原理。后来慢慢了解,看了大量的博客和自己慢慢的总结,摘抄了部分我理解的博客内容,内容基本属于前人总结,自己手动写一遍也是为了加强自身理解。

在知道他们的区别之前,我们首先要知道NSObject对象的赋值操作做了那些操作。A=C其实是在内存中创建一个A,然后又开辟了一个内存C,C里面存放的着值B。

 

 

 

 

示意图1

 

 

如下: 

//给一个NSObject的变量A赋值B,我们其实是创建了一个指针A,然后指向了一个保存数据B的内存,这个内存地址是C。

NSMutableString *tempMStr = [[NSMutableStringalloc]initWithString:@"strValue"];

NSLog(@"tempMStr指针地址:%p,tempMStr值%@ tempMStr值引用计数%@\n", tempMStr,tempMStr,[tempMStr valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.842124+0800 lbCopy[2306:112462] tempMStr指针地址:0x600000057df0,tempMStr值strValue tempMStr值引用计数1

    

此处tempMStr就是A,值地址就是C,“strValue”就是B,而引用计数这个概念就是针对C的,赋值给其他变量或者指针设置为nil,如tempStr = nil,都会使得引用计数有所变化。当内存区域引用计数为0时就会将数据抹除。而我们使用copy,strong,retain,weak,assign区别就在于:此处tempMStr就是A,值地址就是C,“strValue”就是B,而引用计数这个概念就是针对C的,赋值给其他变量或者指针设置为nil,如tempStr = nil,都会使得引用计数有所变化。当内存区域引用计数为0时就会将数据抹除。

而我们使用copy,strong,retain,weak,assign区别就在于:

 

1.是否开辟新的内存

2.是否对地址C有所引用

需要注意的是property修饰符是在被赋值时起作用的

1.以典型的NSMutableString为例

 

@property (copy,nonatomic)NSMutableString *aCopyMStr;

@property (strong,nonatomic)NSMutableString *strongMStr;

@property (weak,nonatomic)NSMutableString *weakMStr;

@property (assign,nonatomic)NSMutableString *assignMStr;

 

 

 

//------------------可变变量实验------------------------"

NSMutableString *mstrOrigin = [[NSMutableStringalloc] initWithString:@"mstrOrigin in heap"];

self.aCopyMStr = mstrOrigin;

self.strongMStr = mstrOrigin;

self.weakMStr = mstrOrigin;

NSLog(@"mstrOrigin输出:指针地址%p,值地址%p,%@\n", &mstrOrigin,mstrOrigin,mstrOrigin);

//2018-01-22 10:13:52.842324+0800 lbCopy[2306:112462] mstrOrigin输出:指针地址0x7ffee8536ae0,值地址0x60c0000573a0,mstrOrigin in heap

    

NSLog(@"aCopyMStr输出:指针地址%p,值地址%p,%@\n", &_aCopyMStr,_aCopyMStr,_aCopyMStr);

//2018-01-22 10:13:52.842431+0800 lbCopy[2306:112462] aCopyMStr输出:指针地址0x7f99fea0af80,值地址0x60c0000492d0,mstrOrigin in heap

    

NSLog(@"strongMStr输出:指针地址%p,值地址%p,%@\n", &_strongMStr,_strongMStr,_strongMStr);

//2018-01-22 10:13:52.842564+0800 lbCopy[2306:112462] strongMStr输出:指针地址0x7f99fea0af88,值地址0x60c0000573a0,mstrOrigin in heap

    

NSLog(@"weakMStr输出:指针地址%p,值地址%p,%@\n", &_weakMStr,_weakMStr,_weakMStr);

//2018-01-22 10:13:52.842729+0800 lbCopy[2306:112462] weakMStr输出:指针地址0x7f99fea0af90,值地址0x60c0000573a0,mstrOrigin in heap

    

NSLog(@"引用计数%@",[mstrOriginvalueForKey:@"retainCount"]);

//2018-01-22 10:13:52.842870+0800 lbCopy[2306:112462] 引用计数2

    

NSLog(@"引用计数%@",[_weakMStrvalueForKey:@"retainCount"]);//?遗留问题,为什么此处_weakMStr是3

//2018-01-22 10:13:52.842989+0800 lbCopy[2306:112462] 引用计数3

    

//------------------结论------------------------"

//"除了copy修饰的aCopyMStr,strongMStr和weakMStr指针指向的内存地址都和mstrOrigin相同,但mstrOrigin内存引用计数为2,不为3,因为weakMStr不会增加指针指向的内存地址的计数指针。aCopyMStr赋值后则是自己单独在堆中开辟了一块内存,内存上保存“mstrOrigin”字符串,然后aCopyMStr指向了mstrOrigin");

 

 

strongMStr和weakMStr指针指向的内存地址都和mstrOrigin相同,但是mstrOrigin内存引用计数为2,不为3,因为weakMStr虽然指向了数据内存地址(之后用C简称,见示意图1),但不会增加C计数。copy修饰的aCopyMStr,赋值后则是自己单独开辟了一块内存,内存上保存“mstrOrigin”字符串,并指向。

 

拷贝示意图如下

 

NSMutableString拷贝示意图2

可见当我修改mstrOrigin的值的时候,必然不会影响aCopyMStr,只会影响strongMStr和weakMStr.我们来验证下

 

 //------------------修改原值后------------------------"

[mstrOrigin appendString:@"1"];

NSLog(@"mstrOrigin输出:%p,%@\n", mstrOrigin,mstrOrigin);

//2018-01-22 10:13:52.843089+0800 lbCopy[2306:112462] mstrOrigin输出:0x60c0000573a0,mstrOrigin in heap1

    

NSLog(@"aCopyMStr输出:%p,%@\n",_aCopyMStr,_aCopyMStr);

//2018-01-22 10:13:52.843202+0800 lbCopy[2306:112462] aCopyMStr输出:0x60c0000492d0,mstrOrigin in heap

    

NSLog(@"strongMStr输出:%p,%@\n",_strongMStr,_strongMStr);

//2018-01-22 10:13:52.843305+0800 lbCopy[2306:112462] strongMStr输出:0x60c0000573a0,mstrOrigin in heap1

    

NSLog(@"weakMStr输出:%p,%@\n",_weakMStr,_weakMStr);

//2018-01-22 10:13:52.843418+0800 lbCopy[2306:112462] weakMStr输出:0x60c0000573a0,mstrOrigin in heap1

    

//------------------结论------------------------

//aCopyMStr值没有改变被修改,其他都随之修改,验证结论1"

 

 

copy会重新开辟新的内存来保存一份相同的数据。被赋值对象和原值修改互不影响。strong和weak赋值都指向原来数据地址,区别是前者会对数据地址进行引用计数+1,后者不会

引用计数是否+1有什么实质区别呢?

如果知道“值地址的引用计数为0时,地址上保存的值就会被释放”。那么区别就不难理解,weak修饰的指针A指向的值地址C,那么地址上当其他指向他的指针被释放的时候,这个值地址引用计数也就变为0了,这个A的值也就为nil了。换句话说当值地址C上没有其他强引用指针修饰的时候C就会被立即释放,A的值就变为nil了。

这里我们来初始化mstrOrigin和并将strongMStr设置为nil让C的引用计数为0,然后输出weakMStr,看是否为nil.

注:初始化和设为nil都可以将指针所指向的数据地址引用计数减少1

 

//------------------修改原指针和strongMStr的指向------------------------

mstrOrigin = [[NSMutableString alloc] initWithString:@"mstrOriginChange2"];

self.strongMStr =nil;

NSLog(@"mstrOrigin输出:%p,%@\n", mstrOrigin,mstrOrigin);

//2018-01-22 10:13:52.843535+0800 lbCopy[2306:112462] mstrOrigin输出:0x60c0000574c0,mstrOriginChange2

    

NSLog(@"strongMStr输出:%p,%@\n",_strongMStr,_strongMStr);

//2018-01-22 10:13:52.843649+0800 lbCopy[2306:112462] strongMStr输出:0x0,(null)

    

NSLog(@"weakMStr输出:%p,%@\n",_weakMStr,_weakMStr);

//2018-01-22 10:13:52.843769+0800 lbCopy[2306:112462] weakMStr输出:0x0,(null)

    

//------------------结论------------------------

//此时weakMStr没有值了,说明C随着strongMStr不再指向他,C被赋值nil,C的引用计数为0,证明结论2,3。 

 

可见之前引用计数2是mstrOrigin和strongMStr添加的。

 

结论:copy会重新开辟新的内存来保存一份相同的数据。被赋值对象和原值修改互不影响。strong和weak虽然都指向原来数据地址,原值修改的时候storng和weak会随之变化。区别是前者会对数据地址进行引用计数+1防止原地址值被释放,但后者不会,当其他值都不在指向值地址时,值地址被释放,weak的值也就是为nil了。我们称会对数据地址增加引用计数的为强引用,不改变引用计数的为弱引用

 

1.2 assign和weak的区别

 

对assign和weak修饰的值进行赋值,并输出指针结构地址和值

 

self.assignMStr = mstrOrigin;

self.weakMStr = mstrOrigin;

NSLog(@"mstrOrigin输出:%p,%@\n", mstrOrigin,mstrOrigin);

//2018-01-22 10:13:52.843874+0800 lbCopy[2306:112462] mstrOrigin输出:0x60c0000574c0,mstrOriginChange2

    

NSLog(@"weakMStr输出:%p,%@\n",_weakMStr,_weakMStr);

//2018-01-22 10:13:52.906916+0800 lbCopy[2306:112462] weakMStr输出:0x60c0000574c0,mstrOriginChange2

    

NSLog(@"assignMStr输出:%p,%@\n",_assignMStr,_assignMStr);

//2018-01-22 10:13:52.907085+0800 lbCopy[2306:112462] assignMStr输出:0x60c0000574c0,mstrOriginChange2

    

mstrOrigin = [[NSMutableString alloc] initWithString:@"mstrOriginChange3"];

    

NSLog(@"mstrOrigin输出:%p,%@\n", mstrOrigin,mstrOrigin);

//2018-01-22 10:13:52.907248+0800 lbCopy[2306:112462] mstrOrigin输出:0x60800005b900,mstrOriginChange3

    

NSLog(@"weakMStr输出:%p,%@\n",_weakMStr,_weakMStr);

//2018-01-22 10:13:52.907377+0800 lbCopy[2306:112462] weakMStr输出:0x0,(null)

    

//NSLog(@"assignMStr输出:%p,%@\n", _assignMStr,_assignMStr);

//------------------结论------------------------"

//可以当mstrOrigin重新初始化后,assignMStr输出偶先奔溃,不奔溃的时候也存在值,且值不为nil。证明结论4"

    

 

    

可以发现在输出assignMStr时会偶尔出现奔溃的情况。原因是发送了野指针的情况。assign同weak,指向C并且计数不+1,但当C地址引用计数为0时,assign不会对C地址进行B数据的抹除操作,只是进行值释放。这就导致野指针存在,即当这块地址还没写上其他值前,能输出正常值,但一旦重新写上数据,该指针随时可能没有值,造成奔溃。

 

1.3那retain是什么

ARC之前属性构造器的关键字是retain,copy,assign,strong和weak是ARC带出来的关键字。

retain现在同strong,就是指针指向值地址,同时进行引用计数加1。

2.非NSMutableString的情况

上面我们讨论了典型的例子NSMutableString,即非容器可变变量。也就是说还存在其他三种类型需要讨论...

 

1.非容器不可变变量NSSting
2.容器可变变量NSMutableArray
3.容器不可变变量NSArray

 

更重要的是不同类型会有不同结果...,好吧,不要奔溃,上面一大段我们讨论了1/4,接下来我们要讨论其他的3/4情况。但好消息是,其他几种情况基本与上面非容器可变变量情况基本类似。

 

2.1容器可变变量

容器可变变量的典型例子就是NSMutableArray
下面代码可以忽略,只做参考用

 

@property (copy,nonatomic)NSMutableArray *aCopyMArr;

@property (strong,nonatomic)NSMutableArray *strongMArr;

@property (weak,nonatomic)NSMutableArray *weakMArr;

@property (assign,nonatomic)NSMutableArray *assignMArr;

 

 

 

//3.可变容器变量,如NSMutableArray。容器本身和可变非容器变量一样。但容器中的数据不管是copy和weak,还是strong都是浅拷贝

NSMutableArray   *mArrOrigin = [[NSMutableArrayalloc] init];

    

NSLog(@"mArrOrigin中的数据引用计数%@", [mArrOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.910967+0800 lbCopy[2306:112462] mArrOrigin中的数据引用计数(

//                                                                            )

    

NSMutableString  *mstr1 = [[NSMutableStringalloc] initWithString:@"value1"];

NSMutableString  *mstr2 = [[NSMutableStringalloc] initWithString:@"value2"];

NSMutableString  *mstr3 = [[NSMutableStringalloc] initWithString:@"value3"];

[mArrOrigin addObject:mstr1];

[mArrOrigin addObject:mstr2];

    

NSLog(@"mArrOrigin中的数据引用计数%@", [mArrOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.911164+0800 lbCopy[2306:112462] mArrOrigin中的数据引用计数(

//                                                                       2,

//                                                                       2

//                                                                       )

    

self.aCopyMArr = mArrOrigin;

self.strongMArr = mArrOrigin;

self.weakMArr = mArrOrigin;

NSLog(@"mArrOrigin输出:%p,%@\n", mArrOrigin,mArrOrigin);

//2018-01-22 10:13:52.911319+0800 lbCopy[2306:112462] mArrOrigin输出:0x60800005b9c0,(

//                                                                                 value1,

//                                                                                 value2

//                                                                                 )

    

NSLog(@"aCopyMArr输出:%p,%@\n",_aCopyMArr,_aCopyMArr);

//2018-01-22 10:13:52.911443+0800 lbCopy[2306:112462] aCopyMArr输出:0x60400003bb60,(

//                                                                                value1,

//                                                                                value2

//                                                                                )

    

NSLog(@"strongMArr输出:%p,%@\n",_strongMArr,_strongMArr);

//2018-01-22 10:13:52.911683+0800 lbCopy[2306:112462] strongMArr输出:0x60800005b9c0,(

//                                                                                 value1,

//                                                                                 value2

//                                                                                 )

    

NSLog(@"weakMArr输出:%p,%@\n",_weakMArr,_weakMArr);

//2018-01-22 10:13:52.911904+0800 lbCopy[2306:112462] weakMArr输出:0x60800005b9c0,(

//                                                                               value1,

//                                                                               value2

//                                                                               )

    

NSLog(@"weakMArr输出:%p,%@\n",_weakMArr[0],_weakMArr[0]);

//2018-01-22 10:13:52.912126+0800 lbCopy[2306:112462] weakMArr输出:0x6040002470e0,value1

    

NSLog(@"mArrOrigin中的数据引用计数%@", [mArrOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.912316+0800 lbCopy[2306:112462] mArrOrigin中的数据引用计数(

//                                                                       3,

//                                                                       3

//                                                                       )

    

NSLog(@"%p %p %p %p",&mArrOrigin,mArrOrigin,mArrOrigin[0],mArrOrigin[1]);

//2018-01-22 10:13:52.912530+0800 lbCopy[2306:112462] 0x7ffee8536ad0 0x60800005b9c0 0x6040002470e0 0x604000246f30

    

[_weakMArr addObject:mstr3];

    

NSLog(@"mArrOrigin输出:%p,%@\n" , mArrOrigin,mArrOrigin);

//2018-01-22 10:13:52.912789+0800 lbCopy[2306:112462] mArrOrigin输出:0x60800005b9c0,(

//                                                                                 value1,

//                                                                                 value2,

//                                                                                 value3

//                                                                                 )

    

NSLog(@"aCopyMArr输出:%p,%@\n",_aCopyMArr,_aCopyMArr);

//2018-01-22 10:13:52.912980+0800 lbCopy[2306:112462] aCopyMArr输出:0x60400003bb60,(

//                                                                                value1,

//                                                                                value2

//                                                                                )

    

NSLog(@"strongMArr输出:%p,%@\n",_strongMArr,_strongMArr);

//2018-01-22 10:13:52.913203+0800 lbCopy[2306:112462] strongMArr输出:0x60800005b9c0,(

//                                                                                 value1,

//                                                                                 value2,

//                                                                                 value3

//                                                                                 )

    

NSLog(@"weakMArr输出:%p,%@\n",_weakMArr,_weakMArr);

//2018-01-22 10:13:52.913500+0800 lbCopy[2306:112462] weakMArr输出:0x60800005b9c0,(

//                                                                               value1,

//                                                                               value2,

//                                                                               value3

//                                                                               )

    

NSLog(@"mArrOrigin中的数据引用计数%@", [mArrOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.913702+0800 lbCopy[2306:112462] mArrOrigin中的数据引用计数(

//                                                                       3,

//                                                                       3,

//                                                                       2

//                                                                       )

    

NSLog(@"-------aCopyMArr输出:%p,%@ %@\n", mstr1,mstr1,[mstr1valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.913886+0800 lbCopy[2306:112462] -------aCopyMArr输出:0x6040002470e0,value1 3

    

[mstr1 appendFormat:@"aaa"];

    

NSLog(@"mArrOrigin输出:%p,%@\n" , mArrOrigin,mArrOrigin);

//2018-01-22 10:13:52.914100+0800 lbCopy[2306:112462] mArrOrigin输出:0x60800005b9c0,(

//                                                                                 value1aaa,

//                                                                                 value2,

//                                                                                 value3

//                                                                                 )

    

NSLog(@"aCopyMArr输出:%p,%@\n",_aCopyMArr,_aCopyMArr);

//2018-01-22 10:13:52.914348+0800 lbCopy[2306:112462] aCopyMArr输出:0x60400003bb60,(

//                                                                                value1aaa,

//                                                                                value2

//                                                                                )

    

NSLog(@"strongMArr输出:%p,%@\n",_strongMArr,_strongMArr);

//2018-01-22 10:13:52.914651+0800 lbCopy[2306:112462] strongMArr输出:0x60800005b9c0,(

//                                                                                 value1aaa,

//                                                                                 value2,

//                                                                                 value3

//                                                                                 )

    

NSLog(@"weakMArr输出:%p,%@\n",_weakMArr,_weakMArr);

//2018-01-22 10:13:52.914872+0800 lbCopy[2306:112462] weakMArr输出:0x60800005b9c0,(

//                                                                               value1aaa,

//                                                                               value2,

//                                                                               value3

//                                                                               )

    

NSLog(@"-------aCopyMArr输出:%p,%@ %@\n", mstr1,mstr1,[mstr1valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.915270+0800 lbCopy[2306:112462] -------aCopyMArr输出:0x6040002470e0,value1aaa 3

    

//------------------结论------------------------"

//发现除了CopyMArr其他数组地址都一样,也就是说除了copy其他都是指针指向C而已,也就是浅拷贝。修改容器本身,只对浅拷贝的值影响,不对copy的值影响

    

NSLog(@"mArrOrigin中的第一个数据输出:%p,%@\n", mArrOrigin[0],mArrOrigin[0]);

//2018-01-22 10:13:52.915447+0800 lbCopy[2306:112462] mArrOrigin中的第一个数据输出:0x6040002470e0,value1aaa

    

NSLog(@"aCopyMArr中的第一个数据输出:%p,%@\n", _aCopyMArr[0],_aCopyMArr[0]);

//2018-01-22 10:13:52.915642+0800 lbCopy[2306:112462] aCopyMArr中的第一个数据输出:0x6040002470e0,value1aaa

    

NSLog(@"strongMArr中的第一个数据输出:%p,%@\n", _strongMArr[0],_strongMArr[0]);

//2018-01-22 10:13:52.915858+0800 lbCopy[2306:112462] strongMArr中的第一个数据输出:0x6040002470e0,value1aaa

    

NSLog(@"weakMArr中的第一个数据输出:%p,%@\n", _weakMArr[0],_weakMArr[0]);

//2018-01-22 10:13:52.916065+0800 lbCopy[2306:112462] weakMArr中的第一个数据输出:0x6040002470e0,value1aaa

    

[mstr1 appendFormat:@"change"];

    

NSLog(@"mArrOrigin中的第一个数据输出:%p,%@\n", mArrOrigin[0],mArrOrigin[0]);

//2018-01-22 10:13:52.916261+0800 lbCopy[2306:112462] mArrOrigin中的第一个数据输出:0x6040002470e0,value1aaachange

    

NSLog(@"aCopyMArr中的第一个数据输出:%p,%@\n", _aCopyMArr[0],_aCopyMArr[0]);

//2018-01-22 10:13:52.916437+0800 lbCopy[2306:112462] aCopyMArr中的第一个数据输出:0x6040002470e0,value1aaachange

    

NSLog(@"strongMArr中的第一个数据输出:%p,%@\n", _strongMArr[0],_strongMArr[0]);

//2018-01-22 10:13:52.916641+0800 lbCopy[2306:112462] strongMArr中的第一个数据输出:0x6040002470e0,value1aaachange

    

NSLog(@"weakMArr中的第一个数据输出:%p,%@\n", _weakMArr[0],_weakMArr[0]);

//2018-01-22 10:13:52.916958+0800 lbCopy[2306:112462] weakMArr中的第一个数据输出:0x6040002470e0,value1aaachange

    

    

//------------------结论------------------------

//容器本身会因为不同修饰符进行有深浅拷贝,但容器中的值地址都是同一个,修改后被赋值的数组都会修改。

    

    

mArrOrigin = nil;

self.aCopyMArr =nil;

self.strongMArr =nil;

    

NSLog(@"mArrOrigin中的第一个数据输出:%p,%@\n", mArrOrigin[0],mArrOrigin[0]);

//2018-01-22 10:13:52.917199+0800 lbCopy[2306:112462] mArrOrigin中的第一个数据输出:0x0,(null)

    

NSLog(@"aCopyMArr中的第一个数据输出:%p,%@\n", _aCopyMArr[0],_aCopyMArr[0]);

//2018-01-22 10:13:52.917363+0800 lbCopy[2306:112462] aCopyMArr中的第一个数据输出:0x0,(null)

    

NSLog(@"strongMArr中的第一个数据输出:%p,%@\n", _strongMArr[0],_strongMArr[0]);

//2018-01-22 10:13:52.917646+0800 lbCopy[2306:112462] strongMArr中的第一个数据输出:0x0,(null)

    

NSLog(@"weakMArr中的第一个数据输出:%p,%@\n", _weakMArr[0],_weakMArr[0]);

//2018-01-22 10:13:52.917928+0800 lbCopy[2306:112462] weakMArr中的第一个数据输出:0x0,(null)

    

    

//------------------结论------------------------"

//当aCopyMArr和strongMArr为nil时,也就是说C只有被weakMArr连接的时候,规则和可变非容器变量,不可变非容器变量一样,weakMarr也会被释放"

    

    

//4.不可变非容器变量,如NSArray。容器本身和不可变非容器变量一样,但容器内的值永远是浅拷贝,自行试验

//    总结

//    copy,strong,weak,assign的区别。

//    可变变量中,copy是重新开辟一个内存,strong,weak,assgin后三者不开辟内存,只是指针指向原来保存值的内存的位置,storng指向后会对该内存引用计数+1,而weak,assgin不会。weak,assgin会在引用保存值的内存引用计数为0的时候值为空,并且weak会将内存值设为nil,assign不会,assign在内存没有被重写前依旧可以输出,但一旦被重写将出现奔溃

//    不可变变量中,因为值本身不可被改变,copy没必要开辟出一块内存存放和原来内存一模一样的值,所以内存管理系统默认都是浅拷贝。其他地方和可变变量一样,如weak修饰的变量同样会在内存引用计数为0时变为nil。

//    容器本身遵守上面准则,但容器内部的每个值都是浅拷贝。

//    综上所述,当创建property构造器创建变量value1的时候,使用copy,strong,weak,assign根据具体使用情况来决定。value1 = value2,如果你希望value1和value2的修改不会互相影响的就用用copy,反之用strong,weak,assign。如果你还希望原来值C为nil的时候,你的变量不为nil就用strong,反之用weak和assign。

    

    

//    实际应用,不同页面之间值如果希望同时被修改就用strong,如果同时修改但希望不强引用就是用weak,如果只是拿这个值就用copy。数组拷贝的时候要注意,里面元素不会深拷贝,如果希望强拷贝

 

 

 

上面代码有点多,所做的操作是mArrOrigin(value1,value2)赋值给copy,strong,weak修饰的aCopyMArr,strongMArr,weakMArr。通过给原数组增加元素,修改原数组元素值,然后输出mArrOrigin的引用计数,和数组地址,查看变化。发现其中数组本身指向的内存地址除了aCopyMArr重新开辟了一块地址,strongMArr,weakMArr和mArrOrigin指针指向的地址是一样的。也就是说

 

 

 

容器可变变量中容器本身和非容器可变变量是一样的,copy深拷贝,strongMArr,weakMArr和assign都是浅拷贝

另外我们发现被拷贝对象mArrOrigin中的数据引用计数居然不是1而是3。也就是说容器内的数据拷贝都是进行了浅拷贝。同时当我们修改数组中的一个数据时strongMArr,weakMArr,aCopyMArr中的数据都改变了,说明容器可变变量中的数据在拷贝的时候都是浅拷贝

容器可变变量的拷贝结构如下图

NSMutableArray拷贝示意图3

 

2.2非容器不变变量

 

典型例子是NSString

我们还是以代码引出结果

 

//2.不可变非容器变量NSString

//不可变量因为变量本身不会变,所以其实任何赋值都没必要重新创建一个块内存,在这个不可变的内存中存放一样的值,节省内存的方式是都指向同一个地方。所以不可变量的拷贝都是浅拷贝,但依旧遵循一个原则就是copy和strong存在时,C依旧不会被释放。但只有weak时值依旧会被释放

//1.copy会不会开辟内存地址,指针指向C,引用计数加1

//2.strong指针指向C,引用计数不加1。

//3.weak指针指向C,引用计数不加1。

//4.assign同weak

    

//dohere  nsstring的copy和strong以及assgin的区别不能通过引用计数来讲,因为nsstring是存在放在_TEXT段的,而引用计数是放在堆内存中的一个整型,对象alloc开辟堆内存空间后,引用计数自动置1;

//_TEXT段:整个程序的代码,以及所有的常量。这部分内存是是固定大小的,只读的。了解_TEXT段内存管理方式,来分析copy ,strong,assgin的区别。

    

//------------------不可变量实验------------------------"

NSString *strOrigin = [[NSStringalloc] initWithUTF8String:"strOrigin0123456"];

    

NSLog(@"strOrigin值内存引用计数%@\n", [_strongStrvalueForKey:@"retainCount"]);

//2018-01-22 10:13:52.907515+0800 lbCopy[2306:112462] strOrigin值内存引用计数(null)

    

self.aCopyStr  = strOrigin;

    

NSLog(@"strOrigin值内存引用计数%@\n", [_strongStrvalueForKey:@"retainCount"]);

//2018-01-22 10:13:52.907636+0800 lbCopy[2306:112462] strOrigin值内存引用计数(null)

    

self.strongStr  = strOrigin;

    

NSLog(@"strOrigin值内存引用计数%@\n", [_strongStrvalueForKey:@"retainCount"]);

//2018-01-22 10:13:52.907773+0800 lbCopy[2306:112462] strOrigin值内存引用计数3

    

NSLog(@"strOrigin值内存引用计数%@\n", [strOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.907905+0800 lbCopy[2306:112462] strOrigin值内存引用计数3

    

self.weakStr = strOrigin;

    

NSLog(@"strOrigin输出:值地址%p,指针地址%p,%@\n", strOrigin,&strOrigin,strOrigin);

//2018-01-22 10:13:52.908019+0800 lbCopy[2306:112462] strOrigin输出:值地址0x60c0000574c0,指针地址0x7ffee8536ad8,strOrigin0123456

    

NSLog(@"aCopyStr输出:值地址%p,指针地址%p,%@\n",  _aCopyStr,&_aCopyStr,_aCopyStr);

//2018-01-22 10:13:52.908205+0800 lbCopy[2306:112462] aCopyStr输出:值地址0x60c0000574c0,指针地址0x7f99fea0afa0,strOrigin0123456

    

NSLog(@"strongStr输出:值地址%p,指针地址%p,%@\n", _strongStr,&_strongStr,_strongStr);

//2018-01-22 10:13:52.908329+0800 lbCopy[2306:112462] strongStr输出:值地址0x60c0000574c0,指针地址0x7f99fea0afa8,strOrigin0123456

    

NSLog(@"weakStr输出:值地址%p,指针地址%p,%@\n", _weakStr,&_weakStr,_weakStr);

//2018-01-22 10:13:52.908525+0800 lbCopy[2306:112462] weakStr输出:值地址0x60c0000574c0,指针地址0x7f99fea0afb0,strOrigin0123456

    

NSLog(@"strOrigin值内存引用计数%@\n", [_strongStrvalueForKey:@"retainCount"]);

//2018-01-22 10:13:52.908681+0800 lbCopy[2306:112462] strOrigin值内存引用计数3

    

//------------------修改原值后------------------------"

    

strOrigin = [[NSString alloc] initWithUTF8String:"aaa"];

    

NSLog(@"strOrigin输出:%p,%@\n", strOrigin,strOrigin);

//2018-01-22 10:13:52.908825+0800 lbCopy[2306:112462] strOrigin输出:0xa000000006161613,aaa

    

NSLog(@"aCopyStr输出:%p,%@\n",_aCopyStr,_aCopyStr);

//2018-01-22 10:13:52.908977+0800 lbCopy[2306:112462] aCopyStr输出:0x60c0000574c0,strOrigin0123456

    

NSLog(@"strongStr输出:%p,%@\n",_strongStr,_strongStr);

//2018-01-22 10:13:52.909098+0800 lbCopy[2306:112462] strongStr输出:0x60c0000574c0,strOrigin0123456

    

NSLog(@"weakStr输出:%p,%@\n",_weakStr,_weakStr);

//2018-01-22 10:13:52.909676+0800 lbCopy[2306:112462] weakStr输出:0x60c0000574c0,strOrigin0123456

    

    

//------------------结论------------------------

//strOrigin值被改变,其他指针指向没有变化。说明不可变类型值不可被修改,只能被重新初始化"

    

    

self.aCopyStr  =nil;

self.strongStr  =nil;

NSLog(@"strOrigin输出:%p,%@\n", strOrigin,strOrigin);

//2018-01-22 10:13:52.909878+0800 lbCopy[2306:112462] strOrigin输出:0xa000000006161613,aaa

    

NSLog(@"aCopyStr输出:%p,%@\n",_aCopyStr,_aCopyStr);

//2018-01-22 10:13:52.910286+0800 lbCopy[2306:112462] aCopyStr输出:0x0,(null)

    

NSLog(@"strongStr输出:%p,%@\n",_strongStr,_strongStr);

//2018-01-22 10:13:52.910488+0800 lbCopy[2306:112462] strongStr输出:0x0,(null)

    

NSLog(@"weakStr输出:%p,%@\n",_weakStr,_weakStr);

//2018-01-22 10:13:52.910705+0800 lbCopy[2306:112462] weakStr输出:0x0,(null)

    

    

//------------------结论------------------------

//当只有weakStr拥有C时,值依旧会被释放,同非容器可变变量

 

 

综合上面现象NSString和NSMutableString(非容器可变变量)基本相同,除了copy,NSString为浅拷贝,NSMutableString是深拷贝。那么为什么NSString的copy是浅拷贝呢,也就是说为什么aCopyStr不自己开辟一个独立的内存出来呢。答案很简单,因为不可变量的值不会改变,既然都不会改变,所以没必要重新开辟一个内存出来让aCopyStr指向他,直接指向原来值位置就可以了。示意图如下

 

所以非容器不可变量除了copy其他特性同非容器可变变量,copy是浅拷贝

2.3不可变容器变量

 

//------------------不可变量实验NSArray------------------------

NSArray *arrayOrigin = [NSArrayarrayWithObjects:@"value1",@"value2",nil];

    

NSLog(@"arrayOrigin值内存引用计数%@\n", [arrayOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.918315+0800 lbCopy[2306:112462] arrayOrigin值内存引用计数(

//                                                                       18446744073709551615,

//                                                                       18446744073709551615

//                                                                       )

    

self.aCopyArr  = arrayOrigin;

    

NSLog(@"arrayOrigin值内存引用计数%@\n", [arrayOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.918580+0800 lbCopy[2306:112462] arrayOrigin值内存引用计数(

//                                                                       18446744073709551615,

//                                                                       18446744073709551615

//                                                                       )

    

self.strongArr  = arrayOrigin;

    

NSLog(@"strongArr值内存引用计数%@\n", [_strongArrvalueForKey:@"retainCount"]);

//2018-01-22 10:13:52.918746+0800 lbCopy[2306:112462] strongArr值内存引用计数(

//                                                                     18446744073709551615,

//                                                                     18446744073709551615

//                                                                     )

    

NSLog(@"arrayOrigin值内存引用计数%@\n", [arrayOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.918928+0800 lbCopy[2306:112462] arrayOrigin值内存引用计数(

//                                                                       18446744073709551615,

//                                                                       18446744073709551615

//                                                                       )

    

self.weakArr = arrayOrigin;

    

NSLog(@"arrayOrigin输出:值地址%p,指针地址%p,%@\n", arrayOrigin,&arrayOrigin,arrayOrigin);

//2018-01-22 10:13:52.919154+0800 lbCopy[2306:112462] arrayOrigin输出:值地址0x608000039480,指针地址0x7ffee8536ab0,(

//                                                                                                    value1,

//                                                                                                    value2

//                                                                                                        )

    

NSLog(@"aCopyArr输出:值地址%p,指针地址%p,%@\n",  _aCopyArr,&_aCopyArr,_aCopyArr);

//2018-01-22 10:13:52.919354+0800 lbCopy[2306:112462] aCopyArr输出:值地址0x608000039480,指针地址0x7f99fea0afe0,(

//                                                                                                    value1,

//                                                                                                     value2

//                                                                                                     )

    

NSLog(@"strongArr输出:值地址%p,指针地址%p,%@\n", _strongArr,&_strongArr,_strongArr);

//2018-01-22 10:13:52.919596+0800 lbCopy[2306:112462] strongArr输出:值地址0x608000039480,指针地址0x7f99fea0afe8,(

//                                                                                                    value1,

//                                                                                                    value2

//                                                                                                      )

    

NSLog(@"weakArr输出:值地址%p,指针地址%p,%@\n", _weakArr,&_weakArr,_weakArr);

//2018-01-22 10:13:52.919788+0800 lbCopy[2306:112462] weakArr输出:值地址0x608000039480,指针地址0x7f99fea0aff0,(

//                                                                                                    value1,

//                                                                                                    value2

//                                                                                                    )

    

NSLog(@"arrayOrigin值内存引用计数%@\n", [arrayOrigin valueForKey:@"retainCount"]);

//2018-01-22 10:13:52.920010+0800 lbCopy[2306:112462] arrayOrigin值内存引用计数(

//                                                                       18446744073709551615,

//                                                                       18446744073709551615

//                                                                       )

    

NSLog(@"arrayOrigin中的第一个数据输出:%p,%@\n", arrayOrigin[0],arrayOrigin[0]);

//2018-01-22 10:13:52.920200+0800 lbCopy[2306:112462] arrayOrigin中的第一个数据输出:0x1076cc3f0,value1

    

NSLog(@"aCopyArr中的第一个数据输出:%p,%@\n", _aCopyArr[0],_aCopyArr[0]);

//2018-01-22 10:13:52.920388+0800 lbCopy[2306:112462] aCopyArr中的第一个数据输出:0x1076cc3f0,value1

    

NSLog(@"strongArr中的第一个数据输出:%p,%@\n", _strongArr[0],_strongArr[0]);

//2018-01-22 10:13:52.920579+0800 lbCopy[2306:112462] strongArr中的第一个数据输出:0x1076cc3f0,value1

    

NSLog(@"weakArr中的第一个数据输出:%p,%@\n", _weakArr[0],_weakArr[0]);

//2018-01-22 10:13:52.920810+0800 lbCopy[2306:112462] weakArr中的第一个数据输出:0x1076cc3f0,value1

    

//------------------修改原值后------------------------

arrayOrigin = [NSArrayarrayWithObjects:@"value11111",@"value22222",nil];

    

NSLog(@"arrayOrigin输出:%p,%@\n", arrayOrigin,arrayOrigin);

//2018-01-22 10:13:52.921018+0800 lbCopy[2306:112462] arrayOrigin输出:0x600000037d00,(

//                                                                                  value11111,

//                                                                                  value22222

//                                                                                  )

    

NSLog(@"aCopyArr输出:%p,%@\n",_aCopyArr,_aCopyArr);

//2018-01-22 10:13:52.921214+0800 lbCopy[2306:112462] aCopyArr输出:0x608000039480,(

//                                                                               value1,

//                                                                               value2

//                                                                               )

    

NSLog(@"strongArr输出:%p,%@\n",_strongArr,_strongArr);

//2018-01-22 10:13:52.921445+0800 lbCopy[2306:112462] strongArr输出:0x608000039480,(

//                                                                                value1,

//                                                                                value2

//                                                                                )

    

NSLog(@"weakArr输出:%p,%@\n",_weakArr,_weakArr);

//2018-01-22 10:13:52.921625+0800 lbCopy[2306:112462] weakArr输出:0x608000039480,(

//                                                                              value1,

//                                                                              value2

//                                                                              )

    

NSLog(@"arrayOrigin中的第一个数据输出:%p,%@\n", arrayOrigin[0],arrayOrigin[0]);

//2018-01-22 10:13:52.921806+0800 lbCopy[2306:112462] arrayOrigin中的第一个数据输出:0x1076cc710,value11111

    

NSLog(@"aCopyArr中的第一个数据输出:%p,%@\n", _aCopyArr[0],_aCopyArr[0]);

//2018-01-22 10:13:52.921976+0800 lbCopy[2306:112462] aCopyArr中的第一个数据输出:0x1076cc3f0,value1

    

NSLog(@"strongArr中的第一个数据输出:%p,%@\n", _strongArr[0],_strongArr[0]);

//2018-01-22 10:13:52.922484+0800 lbCopy[2306:112462] strongArr中的第一个数据输出:0x1076cc3f0,value1

    

NSLog(@"weakArr中的第一个数据输出:%p,%@\n", _weakArr[0],_weakArr[0]);

//2018-01-22 10:13:52.923258+0800 lbCopy[2306:112462] weakArr中的第一个数据输出:0x1076cc3f0,value1

    

    

//------------------结论------------------------

//arrayOrigin值被改变,其他指针指向没有变化。说明不可变类型值不可被修改,只能被重新初始化

    

    

self.aCopyArr  =nil;

self.strongArr  =nil;

    

NSLog(@"arrayOrigin输出:%p,%@\n", arrayOrigin,arrayOrigin);

//2018-01-22 10:13:52.923495+0800 lbCopy[2306:112462] arrayOrigin输出:0x600000037d00,(

//                                                                                  value11111,

//                                                                                  value22222

//                                                                                  )

    

NSLog(@"aCopyArr输出:%p,%@\n",_aCopyArr,_aCopyArr);

//2018-01-22 10:13:52.923885+0800 lbCopy[2306:112462] aCopyArr输出:0x0,(null)

    

NSLog(@"strongArr输出:%p,%@\n",_strongArr,_strongArr);

//2018-01-22 10:13:52.924110+0800 lbCopy[2306:112462] strongArr输出:0x0,(null)

    

NSLog(@"weakArr输出:%p,%@\n",_weakArr,_weakArr);

//2018-01-22 10:13:52.924592+0800 lbCopy[2306:112462] weakArr输出:0x608000039480,(

//                                                                              value1,

//                                                                              value2

//                                                                              )

    

NSLog(@"arrayOrigin中的第一个数据输出:%p,%@\n", arrayOrigin[0],arrayOrigin[0]);

//2018-01-22 10:13:52.924851+0800 lbCopy[2306:112462] arrayOrigin中的第一个数据输出:0x1076cc710,value11111

    

NSLog(@"aCopyArr中的第一个数据输出:%p,%@\n", _aCopyArr[0],_aCopyArr[0]);

//2018-01-22 10:13:52.925057+0800 lbCopy[2306:112462] aCopyArr中的第一个数据输出:0x0,(null)

    

NSLog(@"strongArr中的第一个数据输出:%p,%@\n", _strongArr[0],_strongArr[0]);

//2018-01-22 10:13:52.925264+0800 lbCopy[2306:112462] strongArr中的第一个数据输出:0x0,(null)

    

NSLog(@"weakArr中的第一个数据输出:%p,%@\n", _weakArr[0],_weakArr[0]);

//2018-01-22 10:13:52.926248+0800 lbCopy[2306:112462] weakArr中的第一个数据输出:0x1076cc3f0,value1

    

//------------------结论------------------------

//当只有weakArr拥有C时,值依旧会被释放,同非容器可变变量

 

 

 

典型对象NSArray。该对象实验自行实验。但结论在这里给出,其实不实验也可以大概知道概率
在不可变容器变量中,容器本身都是浅拷贝包括copy,同NSString,容器里面的数据都是浅拷贝,同NSMutableArray。

3.总结
copy,strong,weak,assign的区别。

可变变量中,copy是重新开辟一个内存,strong,weak,assgin后三者不开辟内存,只是指针指向原来保存值的内存的位置,storng指向后会对该内存引用计数+1,而weak,assgin不会。weak,assgin会在引用保存值的内存引用计数为0的时候值为空,并且weak会将内存值设为nil,assign不会,assign在内存没有被重写前依旧可以输出,但一旦被重写将出现奔溃。

不可变变量中,因为值本身不可被改变,copy没必要开辟出一块内存存放和原来内存一模一样的值,所以内存管理系统默认都是浅拷贝。其他和可变变量一样,如weak修饰的变量同样会在内存引用计数为0时变为nil。

容器本身遵守上面准则,但容器内部的每个值都是浅拷贝。

**综上所述,当创建property构造器创建变量value1的时候,使用copy,strong,weak,assign根据具体使用情况来决定。value1 = value2,如果你希望value1和value2的修改不会互相影响的就用用copy,反之用strong,weak,assign。如果你还希望原来值C(C是什么见示意图1)为nil的时候,你的变量不为nil就用strong,反之用weak和assign。weak和assign保证了不强引用某一块内存,如delegate我们就用weak表示,就是为了防止循环引用的产生。

另外,我们上面讨论的是类变量,直接创建局部变量默认是Strong修饰

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值