强引用和弱引用:
我们已经知道OC中的内存管理是通过“引用计数器”来实现的。一个对象的生命周期取决于它是否还被其他对象引用(是否retainCount=0)。但在有些情况下,我们并不希望对象的销毁时间由是否被其他对象引用来决定,而是这个对象本该是什么时候销毁就什么时候被销毁。这时,我们得引入“强引用”和“弱引用”的概念。
强引用:当前对象被其他对象引用时,会执行retain操作,引用计数器+1。当retainCount=0时,该对象才会被销毁。因为我们要进行对象的内存管理,所以这是默认的引用方式。(默认是强引用)
弱引用:当前对象的生命周期不被是否由其他对象引用限制,它本该什么时候销毁就什么时候被销毁。即使它的引用没断,但是当它的生存周期到了时就会被销毁。
在定义属性时,若声明为retain类型的,则就是强引用;若声明为assign类型的,则就是弱引用。后来内存管理都由ARC来完成后,若是强引用,则就声明为strong;若是弱引用,则就声明为weak。
所以说,retain和strong是一致的(声明为强引用);assign和weak是基本一致的(声明为弱引用)。之所以说它俩是基本一致是因为它俩还是有所不同的,weak严格的说应当叫“归零弱引用”,即当对象被销毁后,会自动的把它的指针置为nil,这样可以防止野指针错误。而assign销毁对象后不会把该对象的指针置nil,对象已经被销毁,但指针还在痴痴的指向它,这就成了野指针,这是比较危险的。
避免“强引用循环“的僵局:
默认的引用方式是强引用,但上面说了有时我们还得使用弱引用,那是什么情况呢?
答案,强引用循环:A对象强引用了B对象,B对象也强引用了A。因为都是强引用,也就是无论是A是B都要在对方的引用断了后才能销毁,但要断了引用,就必须对方对象销毁。就会出现这种僵局,为了避免出现这种情况,就应该有一个对象“示弱”,使其为“弱引用”。
比较常见的,视图中的父子视图之间的引用:父视图强引用子视图,子视图弱引用父视图。
总结:由于要进行内存管理的缘故,OC里的引用默认都是强引用,但为了避免出现”强引用循环僵局“,所以有了弱引用(assign)。
关于copy:参考链接
retain和strong都是指针拷贝。当有其他对象引用当前对象时,会拷贝一份当前对象的地址,这样它就也指向当前对象了。所以,还是同一个对象,只是retainCount+1;
而copy则是内容拷贝。是实实在在的拷贝一个新的对象,拷贝了它的内存内容,成为一个新的对象(retainCount=1)。
深拷贝就是内容拷贝,浅拷贝就是指针拷贝。
在OC中,若要进行对象的拷贝,则该对象所属的类必须遵守NSCopying和NSMutableCopy协议,并重写copyWithZone:和mutableCopyWithZone:方法。而系统原生类,之所以可以直接进行拷贝是因为它已帮我们自动做了这些事。
不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准则:
copy返回imutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;
mutableCopy返回mutable对象;
1.下面将针对非集合类对象和集合类对象的copy和mutableCopy方法进行具体的阐述
系统非集合类对象指的是NSString, NSNumber ...之类的对象。下面先看个非集合类immutable对象拷贝的例子
NSString*string =@"origin";
NSString*stringCopy = [stringcopy];
NSMutableString*stringMCopy= [stringmutableCopy];
NSLog(@"string %p",string);
NSLog(@"stringCopy %p",stringCopy);
NSLog(@"stringMCopy %p--------------------------",stringMCopy);
通过查看内存,可以看到stringCopy和string的地址是一样,进行了指针拷贝;
而stringMCopy的地址和string不一样,进行了内容拷贝;
NSMutableString*string = [NSMutableStringstringWithString:@"origin"];
//copy
NSString*stringCopy = [stringcopy];
NSMutableString*mStringCopy = [stringcopy];
NSMutableString*stringMCopy = [stringmutableCopy];
//change value
//[mStringCopy appendString:@"mm"]; //crash
[stringappendString:@" origion!"];
[stringMCopyappendString:@"!!"];
NSLog(@"动态字符串string %p",string);
NSLog(@"stringCopy %p",stringCopy);
NSLog(@"mStringCopy %p",mStringCopy);
NSLog(@"stringMCopy %p",stringMCopy);
运行以上代码,会在[mStringCopy appendString:@"mm"];行crash,原因就是copy返回的对象是immutable对象。注释这一行后再运行,查看内存,发现string、stringCopy、mStringCopy、stringMCopy四个对象的内存地址都不一样,说明此时都是做内容拷贝。
在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;
对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
[immutableObject copy] //浅复制
[immutableObject mutableCopy] //深复制
[mutableObject copy] //深复制
[mutableObject mutableCopy] //深复制
2.集合对象的copy与mutableCopy
集合类对象是指NSArray、NSDictionary、NSSet ...之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子:
NSArray*array =@[@[@"a",@"b"],@[@"c",@"d"]];
NSArray*copyArray = [arraycopy];
NSMutableArray*mCopyArray =[arraymutableCopy];
NSLog(@"array %p",array);
NSLog(@"coptArray %p",copyArray);
NSLog(@"mcoptArray %p", mCopyArray);
查看内容,可以看到copyArray和array的地址是一样的,而mCopyArray和array的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。这和上面的非集合immutable对象的拷贝还是挺相似的,那么mutable对象的拷贝会不会类似呢?我们继续往下,看mutable对象拷贝的例子:
NSMutableArray*array = [NSMutableArrayarrayWithObjects:[NSMutableStringstringWithString:@"a"],@"b",@"c",nil];
NSArray*copyArray = [arraycopy];
NSMutableArray*mCopyArray = [arraymutableCopy];
NSLog(@"array %p",array);
NSLog(@"coptArray %p",copyArray);
NSLog(@"mcoptArray %p", mCopyArray);
查看内存,如我们所料,copyArray、mCopyArray和array的内存地址都不一样,说明copyArray、mCopyArray都对array进行了内容拷贝。同样,我们可以得出结论:
在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:
[immutableObject copy] //浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制
网友评论