
淺拷貝與深拷貝
淺拷貝:指針拷貝,不產生新的對象,源對象的引用計數器+1
-
深拷貝:對象拷貝,會產生新的對象,源對象的引用計數器不變
淺拷貝與深拷貝
copy與mutableCopy
-
copy:拷貝的結果是一個不可變(imutable)的對象, 無論源對象是可變的還是不可變的,copy之后的都是不可變的類型
不可變類型 變量名 = [不可變類型 copy];
不可變類型 變量名 = [可變類型 copy];
-
mutableCopy:可變拷貝的結果的數據類型是一個可變的對象,無論源對象時不可變的還是可變的,可變拷貝之后的數據類型都是可變類型
可變類型 變量名 = [不可變類型 mutableCopy];
可變類型 變量名 = [可變類型 mutableCopy];
-
copy對引用計數器的影響:
拷貝一個不可變的類型的結果是新對象和源對象都指向同一個內存地址,即使指針拷貝,屬于淺拷貝,所以不生產新對象,源對象的引用計數+1
拷貝一個可變的類型,會生成一個新對象,不影響源對象的引用計數
-
mutableCopy對引用計數器的影響:
- 無論對可變類型或者對不可變類型使用mutableCopy操作,都不會影響源對象的引用計數
copy與mutableCopy的使用
-
- 系統(tǒng)非容器類對象
- 不可變類型
NSString *str = @"123"; NSString *strCopy = [str copy]; NSMutableString *mustr = [str copy]; NSMutableString *mustrCopy = [str mutableCopy]; // [mustr appendString:@"4"]; //會崩潰 [mustrCopy appendString:@"5"]; NSLog(@"str = %@,str地址 = %p,strCopy = %@,strCopy地址 = %p ",str,str,strCopy,strCopy); NSLog(@"mustr = %@,mustr地址 = %p,mustrCopy = %@,mustrCopy地址 = %p",mustr ,mustr,mustrCopy,mustrCopy); /** str = 123,str地址 = 0x102ad4098,strCopy = 123,strCopy地址 = 0x102ad4098 mustr = 123,mustr地址 = 0x102ad4098,mustrCopy = 1235,mustrCopy地址 = 0x1c444bca0 */- 可變類型
NSMutableString *mustr1 = [NSMutableString stringWithFormat:@"123"]; NSString *immutableStrCopy = [mustr1 copy]; NSMutableString *mutableStr = [mustr1 copy]; NSMutableString *mutableStrCopy = [mustr1 mutableCopy]; // [mutableStr appendString:@"5"]; //會崩潰 [mutableStrCopy appendString:@"6"]; NSLog(@"mustr1 = %@,mustr1地址 = %p,immutableStrCopy = %@,immutableStrCopy地址 = %p ",mustr1,mustr1,immutableStrCopy,immutableStrCopy); NSLog(@"mutableStr = %@,mutableStr地址 = %p,mutableStrCopy = %@,mutableStrCopy地址 = %p",mutableStr ,mutableStr,mutableStrCopy,mutableStrCopy); /** mustr1 = 123,mustr1地址 = 0x1c0247c80,immutableStrCopy = 123,immutableStrCopy地址 = 0xa000000003332313 mutableStr = 123,mutableStr地址 = 0xa000000003332313,mutableStrCopy = 1236,mutableStrCopy地址 = 0x1c0247c20 */總結
對系統(tǒng)非容器類不可變對象調用Copy方法其實只是把當前對象的指針指向了原對象的地址。
調用mutableCopy方法則是新分配了一塊內存區(qū)域并把新對象的指針指向了這塊區(qū)域。
對于可變對象來說調用Copy和MutableCopy方法都會重新分配一塊內存。
copy和mutableCopy的區(qū)別在于copy在復制對象的時候其實是返回了一個不可變對象,因此當調用方法改變對象的時候會崩潰。
-
- 系統(tǒng)容器類對象
-
不可變類型
NSArray *array = [NSArray arrayWithObjects:@"1",@"2",@"3", nil]; NSArray *arrayCopy = [array copy]; NSMutableArray *musarray = [array copy]; NSMutableArray *musarrayCopy = [array mutableCopy]; NSLog(@"array = %@,array地址 = %p,arrayCopy = %@,arrayCopy地址 = %p ",array,array,arrayCopy,arrayCopy); NSLog(@"musarray = %@,musarray地址 = %p,musarrayCopy = %@,musarrayCopy地址 = %p",musarray ,musarray,musarrayCopy,musarrayCopy); /** array = ( 1, 2, 3 ),array地址 = 0x1c44508f0, arrayCopy = ( 1, 2, 3 ),arrayCopy地址 = 0x1c44508f0 musarray = ( 1, 2, 3 ),musarray地址 = 0x1c44508f0, musarrayCopy = ( 1, 2, 3 ),musarrayCopy地址 = 0x1c4653c20 */ -
可變類型
NSMutableArray *muarray = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"1"],@"2",@"3", nil]; NSArray *immutableArrayCopy = [muarray copy]; NSArray *mutableArrayCopy = [muarray mutableCopy]; NSMutableArray *mutArray = [muarray copy]; NSMutableArray *mutArrayCopy = [muarray mutableCopy]; NSLog(@"muarray = %@,muarray地址 = %p,immutableArrayCopy = %@,immutableArrayCopy地址 = %p,mutableArrayCopy = %@, mutableArrayCopy地址 = %p",muarray,muarray,mutableArrayCopy,mutableArrayCopy,immutableArrayCopy,immutableArrayCopy); NSLog(@"mutArray = %@,mutArray地址 = %p,mutArrayCopy = %@,mutArrayCopy地址 = %p",mutArray ,mutArray,mutArrayCopy,mutArrayCopy); /** muarray = ( 1, 2, 3 ),muarray地址 = 0x6040004434b0, immutableArrayCopy = ( 1, 2, 3 ),immutableArrayCopy地址 = 0x604000443750, mutableArrayCopy = ( 1, 2, 3 ), mutableArrayCopy地址 = 0x604000443540 mutArray = ( 1, 2, 3 ),mutArray地址 = 0x604000443ba0, mutArrayCopy = ( 1, 2, 3 ),mutArrayCopy地址 = 0x604000443600 */ //對于可變對象不管調用Copy還是MutableCopy都是新分配一塊內存。但是雖然重新分配了一塊內存,但是對象里面的數據依然是指針復制的 NSMutableString *mustr = muarray[0]; [mustr appendString:@"2"]; NSLog(@"muarray = %@,muarray地址 = %p,immutableArrayCopy = %@,immutableArrayCopy地址 = %p,mutableArrayCopy = %@, mutableArrayCopy地址 = %p",muarray,muarray,mutableArrayCopy,mutableArrayCopy,immutableArrayCopy,immutableArrayCopy); NSLog(@"mutArray = %@,mutArray地址 = %p,mutArrayCopy = %@,mutArrayCopy地址 = %p",mutArray ,mutArray,mutArrayCopy,mutArrayCopy); /** muarray = ( 12, 2, 3 ),muarray地址 = 0x6040004434b0, immutableArrayCopy = ( 12, 2, 3 ),immutableArrayCopy地址 = 0x604000443750, mutableArrayCopy = ( 12, 2, 3 ), mutableArrayCopy地址 = 0x604000443540 mutArray = ( 12, 2, 3 ),mutArray地址 = 0x604000443ba0, mutArrayCopy = ( 12, 2, 3 ),mutArrayCopy地址 = 0x604000443600 */ //可以看到當更改原數組的值之后,所有新數組的值都更改了,即使調用了MutableCopy方法創(chuàng)建的新數組里面的值也因此更改,所以可以看出對于系統(tǒng)容器類對象,其元素對象始終是指針復制 -
總結
對系統(tǒng)容器類不可變對象調用Copy方法其實只是把當前對象的指針指向了原對象的地址。
調用mutableCopy方法則是新分配了一塊內存區(qū)域并把新對象的指針指向了這塊區(qū)域。
對于系統(tǒng)容器類可變對象來說調用Copy和MutableCopy方法都會重新分配一塊內存。
對于系統(tǒng)容器類可變對象,雖然重新分配了一塊內存,但是對象里面的數據依然是指針復制的。
-
- 自定義對象
在iOS中并不是所有對象都支持Copy和MutableCopy,遵循NSCopying協議的類可以發(fā)送Copy協議,遵循NSMutableCopying協議的類可以發(fā)送MutableCopy消息。
如果一個對象沒有遵循這兩個協議而發(fā)送Copy或者MutableCopy消息那么會發(fā)生異常。
如果要遵循NSCopying協議,那么必須實現copyWithZone方法。
如果要遵循NSMutableCopying協議那么必須實現mutableCopyWithZone方法。
對于自定義對象來說調用Copy和MutableCopy方法都會重新分配一塊內存。
// Man.h
#import <Foundation/Foundation.h>
@interface Man : NSObject<NSCopying,NSMutableCopying>
@property (nonatomic,strong)NSString *name;
@property (nonatomic,assign)NSInteger year;
@end
// Man.m
#import "Man.h"
@implementation Man
#pragma mark description方法內部不能打印self,不然會造成死循環(huán)
- (NSString *)description {
return [NSString stringWithFormat:@"[name = %@,year = %ld]", _name,_year];
}
//自定義深拷貝,實現copyWithZone方法
-(id)copyWithZone:(NSZone *)zone{
Man *newMan = [[[self class] allocWithZone:zone] init];
newMan.name = self.name;
newMan.year = self.year;
return newMan;
}
-(id)mutableCopyWithZone:(NSZone *)zone{
Man *newMan = [[[self class] allocWithZone:zone] init];
newMan.name = self.name;
newMan.year = self.year;
return newMan;
}
@end
//調用
Man *man = [[Man alloc]init];
man.name = @"張三";
man.year = 1;
Man *newMan = [man copy];
Man *newMutMan = [man mutableCopy];
NSLog(@"man = %@,man地址 = %p,newMan = %@,newMan地址 = %p,newMutMan = %@, newMutMan地址 = %p",man,man,newMan,newMan,newMutMan,newMutMan);
/**
man = [name = 張三,year = 1],
man地址 = 0x604000036900,
newMan = [name = 張三,year = 1],
newMan地址 = 0x6040004207e0,
newMutMan = [name = 張三,year = 1],
newMutMan地址 = 0x60400003c2a0
*/
newMan.name = @"李四";
NSLog(@"man = %@,man地址 = %p,newMan = %@,newMan地址 = %p,newMutMan = %@, newMutMan地址 = %p",man,man,newMan,newMan,newMutMan,newMutMan);
/**
man = [name = 張三,year = 1],
man地址 = 0x604000036900,
newMan = [name = 李四,year = 1],
newMan地址 = 0x6040004207e0,
newMutMan = [name = 張三,year = 1],
newMutMan地址 = 0x60400003c2a0
*/
newMutMan.name = @"王五";
NSLog(@"man = %@,man地址 = %p,newMan = %@,newMan地址 = %p,newMutMan = %@, newMutMan地址 = %p",man,man,newMan,newMan,newMutMan,newMutMan);
/**
man = [name = 張三,year = 1],
man地址 = 0x604000036900,
newMan = [name = 李四,year = 1],
newMan地址 = 0x6040004207e0,
newMutMan = [name = 王五,year = 1],
newMutMan地址 = 0x60400003c2a0
*/
-
- 總結
-
非集合類對象的 copy 操作:
- [immutableObject copy] 淺復制
- [immutableObject mutableCopy] 深復制
- [mutableObject copy] 深復制
- [mutableObject mutableCopy] 深復制
-
集合類的對象進行 copy 操作:
- [immutableObject copy] 淺復制(對象地址和被復制的對象地址一樣)
- [immutableObject mutableCopy] 單層深復制
- [mutableObject copy] 單層深復制
- [mutableObject mutableCopy] 單層深復制
-
自定義對象
如果要遵循NSCopying協議,那么必須實現copyWithZone方法。
如果要遵循NSMutableCopying協議那么必須實現mutableCopyWithZone方法。
[customObject copy] 深復制
[customObject mutableCopy] 深復制
block為什么要使用copy
block使用copy是在MRC中延續(xù)下來的,在MRC下,方法內部的block是存放在棧區(qū),使用copy會將block拷貝到堆區(qū)。
在ARC下編譯器會自動對block進行copy,因此我們使用copy或者strong的效果是一樣的。但是我們在ARC下繼續(xù)使用copy可以提醒我們編譯器會自動幫我們實現copy的操作。
參考
iOS 淺拷貝和深拷貝的區(qū)別? copy和mutableCopy的區(qū)別?
