iOS详解assign、weak、retain、strong、copy、mutableCopy

先讲讲assign、retain、strong、weak

  • assign
@property (nonatomic,assign)NSMutableArray *arr;

self.arr = [NSMutableArray array];
[self.arr addObject:@"11"];

//运行结果:
Thread 1: EXC_BAD_ACCESS 

这里会直接报内存错误,原因是因为,assign是用来修饰基本数据类型的,例如CGFloat、NSInteger、Int、float等等,因为在OC中assign修饰的对象引用计数不会加+1,这个对象会被立即释放,而且在释放之后不会置为nil,会保留对象的指针,会形成野指针,这个时候对其发送消息就会崩溃,就会报坏内存的错误。

  • weak
@property (nonatomic,weak)NSMutableArray *weakArr;

self.weakArr = [NSMutableArray array];
[self.weakArr addObject:@"1"];
NSLog(@"%@",self.weakArr);


//运行结果:
2018-09-19 16:51:47.901018+0800 Demo[1649:254177] (null)

在OC中Weak用来修饰对象的,如果用来修饰基本数据类型会报红,用weak修饰的对象在引用完毕后会被立即置为nil 而OC向nil发送消息是不会崩溃的。
这里提一下assign和weak修饰的对象的引用计数都是不会加1的

  • retain和strong
@property (nonatomic,strong)NSMutableArray *strongArr;

self.strongArr = [NSMutableArray array];
[self.strongArr addObject:@"11"];
NSLog(@"%@",self.strongArr);

//运行结果:
2018-09-19 16:56:57.389876+0800 Demo[1652:255035] (
    11
)

[NSMutableArray array]这句代码可以理解为创造了一个对象A,此时A的引用计数为1,self.strongArr做为对象B,把A赋值给B的时候,A的引用计数加1,此时A的引用计数为2,B指向了A,然后编译器会自动对A进行释放操作(因为是局部变量),A的引用计数-1。在拥有B的对象不释放的时候,A的引用计数永远不可能为0,除非你手动释放或者把B指向一个新的对象,这样A永远不会被释放,这就是所谓的强引用。retain是在MRC中用到的修饰词,在ARC中便用strong代替了。retain现在同strong,就是指针指向值地址,同时进行引用计数加1。

因为copy涉及的情况比较多,所以接下来单独讲讲copy,主要分为以下几种情况

在说copy与mutableCopy之前我们先看看官方文档对深拷贝与浅拷贝的阐释,如下

iOS详解assign、weak、retain、strong、copy、mutableCopy_第1张图片

深拷贝:
对象拷贝 - 重新申请一片内存保留这个对象,与原对象之间没有半点关系。

浅拷贝:
指针拷贝 - 实际上相当于引用计数+1,被拷贝的和拷贝的引用同一个对象。
接下来我们分两个方面做测试:

  • 非集合不可变对象(copy、mutableCopy)
    NSString *str = [NSString stringWithFormat:@"111"];
    NSString *copyStr = [str copy];
    NSString *mCopyStr = [str mutableCopy];
    NSLog(@"%@ ---- %p",str,str);
    NSLog(@"%@ ---- %p",copyStr,copyStr);
    NSLog(@"%@ ---- %p",mCopyStr,mCopyStr);
    str = @"222";
    NSLog(@"%@ ---- %p",str,str);
    NSLog(@"%@ ---- %p",copyStr,copyStr);
    NSLog(@"%@ ---- %p",mCopyStr,mCopyStr);


//运行结果:
2018-09-19 17:25:12.102270+0800 Demo[1689:260186] 111 ---- 0xa000000003131313
2018-09-19 17:25:12.102387+0800 Demo[1689:260186] 111 ---- 0xa000000003131313
2018-09-19 17:25:12.102418+0800 Demo[1689:260186] 111 ---- 0x2827aa9d0
2018-09-19 17:25:12.102445+0800 Demo[1689:260186] 222 ---- 0x100776020
2018-09-19 17:25:12.102491+0800 Demo[1689:260186] 111 ---- 0xa000000003131313
2018-09-19 17:25:12.102518+0800 Demo[1689:260186] 111 ---- 0x2827aa9d0

这里我们可以看到str和copyStr的地址是相同的,而mCopyStr是开辟了新的内存的。在str = @"222"这句代码后,因为str是不可变的、不可被修改,所以重新初始化。而这里的copy为浅拷贝,mutableCopy为深拷贝。

  • 非集合可变对象(copy、mutableCopy)
    NSMutableString *mStr = [NSMutableString stringWithFormat:@"111"];
    NSString *copyStr = [mStr copy];
    NSString *mCopyStr = [mStr mutableCopy];
    NSLog(@"%@ ---- %p",mStr,mStr);
    NSLog(@"%@ ---- %p",copyStr,copyStr);
    NSLog(@"%@ ---- %p",mCopyStr,mCopyStr);
    [mStr appendString:@",222"];
    NSLog(@"%@ ---- %p",mStr,mStr);
    NSLog(@"%@ ---- %p",copyStr,copyStr);
    NSLog(@"%@ ---- %p",mCopyStr,mCopyStr);


//运行结果:
2018-09-19 17:38:08.698067+0800 Demo[1696:261980] 111 ---- 0x2831745a0
2018-09-19 17:38:08.698234+0800 Demo[1696:261980] 111 ---- 0xa000000003131313
2018-09-19 17:38:08.698280+0800 Demo[1696:261980] 111 ---- 0x283174450
2018-09-19 17:38:08.698323+0800 Demo[1696:261980] 111,222 ---- 0x2831745a0
2018-09-19 17:38:08.698391+0800 Demo[1696:261980] 111 ---- 0xa000000003131313
2018-09-19 17:38:08.698432+0800 Demo[1696:261980] 111 ---- 0x283174450

对非集合可变对象进行copy、mutableCopy时,可以看到其内存地址都发生了变化,所以这里的copy、mutableCopy都为深拷贝,对原有mStr操作,不会影响拷贝之后的值。

  • 集合可变对象(copy、mutableCopy)
    NSMutableArray *arrM = [NSMutableArray arrayWithObject:@"11"];
    NSMutableArray *copyArrM = [arrM copy];
    NSMutableArray *mCopyArrM = [arrM mutableCopy];
    NSLog(@"%@ ---- \n%p",arrM,arrM);
    NSLog(@"%@ ---- \n%p",mCopyArrM,mCopyArrM);
    NSLog(@"%@ ---- \n%p",copyArrM,copyArrM);
    [arrM addObject:@"22"];
    NSLog(@"%@ ---- \n%p",arrM,arrM);
    NSLog(@"%@ ---- \n%p",mCopyArrM,mCopyArrM);
    NSLog(@"%@ ---- \n%p",copyArrM,copyArrM);


//运行结果:
2018-09-19 17:52:49.603180+0800 Demo[1708:264782] (
    11
) ---- 
0x282316d90
2018-09-19 17:52:49.603424+0800 Demo[1708:264782] (
    11
) ---- 
0x282317540
2018-09-19 17:52:49.603533+0800 Demo[1708:264782] (
    11
) ---- 
0x282f4c6a0
2018-09-19 17:52:49.603603+0800 Demo[1708:264782] (
    11,
    22
) ---- 
0x282316d90
2018-09-19 17:52:49.603660+0800 Demo[1708:264782] (
    11
) ---- 
0x282317540
2018-09-19 17:52:49.603716+0800 Demo[1708:264782] (
    11
) ---- 
0x282f4c6a0
  • 集合不可变对象(copy、mutableCopy)
    NSArray *arr = [NSArray arrayWithObject:@"11"];
    NSArray *copyArr = [arr copy];
    NSMutableArray *mCopyArr = [arr mutableCopy];
    NSLog(@"%@ ---- \n%p",arr,arr);
    NSLog(@"%@ ---- \n%p",copyArr,copyArr);
    NSLog(@"%@ ---- \n%p",mCopyArr,mCopyArr);

//运行结果:
2018-09-19 17:57:24.646701+0800 Demo[1717:266299] (
    11
) ---- 
0x2808b81d0
2018-09-19 17:57:24.646878+0800 Demo[1717:266299] (
    11
) ---- 
0x2808b81d0
2018-09-19 17:57:24.647042+0800 Demo[1717:266299] (
    11
) ---- 
0x2804e1ef0

其实对集合不可变对象、集合可变对象进行copy和mutableCopy结果可以参考非集合不可变对象、非集合可变对象的结果

最后对delegate为什么要用weak修饰简单谈下个人的看法

delegate在平常的使用中一般如下:

iOS详解assign、weak、retain、strong、copy、mutableCopy_第2张图片

VC中拥有一个View,那么View的引用计数+1,而View中有delegate,那么delegate的引用计数+1。接着我们会在VC中有 view.delegate = self,那么如果view用strong修饰,此时引用计数+1,此时view的引用计数为2。那么当VC销毁时,view的引用计数-1,此时view的引用计数为1。那么view是无法销毁的,然后view又引用着delegate,所以view和delegate一直会在内存中的。那么如果用weak修饰delegate的,view的引用计数就为1,等到VC销毁的时候,view计数-1,也回跟着销毁,那么view引用的delegate也会销毁。
以上是个人的一点愚见,如有错误,欢迎大家指正。

你可能感兴趣的:(iOS详解assign、weak、retain、strong、copy、mutableCopy)