1. 屬性中copy與strong特性的區(qū)別
在開(kāi)始學(xué)習(xí)淺復(fù)制(Shallow Copy)、深復(fù)制(Deep Copy)之前,先了解下屬性中copy與strong特性的區(qū)別。
copy特性如下:
- copy:創(chuàng)建一個(gè)對(duì)象的副本。在創(chuàng)建的那一刻新對(duì)象與原始對(duì)象內(nèi)容相同。
- 新的對(duì)象引用計(jì)數(shù)為1,與原始對(duì)象引用計(jì)數(shù)無(wú)關(guān),且原始對(duì)象引用計(jì)數(shù)不會(huì)改變。
- 使用copy創(chuàng)建的新對(duì)象也是強(qiáng)引用,使用完成后需要負(fù)責(zé)釋放該對(duì)象。
-
copy特性可以減少對(duì)象對(duì)上下文的依賴(lài)。新對(duì)象、原始對(duì)象中任一對(duì)象的值改變不會(huì)影響另一對(duì)象的值。 - 要想設(shè)置該對(duì)象的特性為
copy,該對(duì)象必須遵守NSCopying協(xié)議,F(xiàn)oundation類(lèi)默認(rèn)實(shí)現(xiàn)了NSCopying協(xié)議,所以只需要為自定義的類(lèi)實(shí)現(xiàn)該協(xié)議即可。
strong特性如下:
- 創(chuàng)建一個(gè)強(qiáng)引用的指針,引用對(duì)象引用計(jì)數(shù)加1。
-
strong特性表示兩個(gè)對(duì)象內(nèi)存地址相同(建立一個(gè)指針,進(jìn)行指針拷貝),內(nèi)容會(huì)一直保持相同,直到更改一方內(nèi)存地址,或?qū)⑵湓O(shè)置為nil。 - 如果有多個(gè)對(duì)象同時(shí)引用一個(gè)屬性,任一對(duì)象對(duì)該屬性的修改都會(huì)影響其他對(duì)象獲取的值。
如果想要對(duì)屬性中特性進(jìn)行更全面了解,可以查看 iOS中定義屬性時(shí)的atomic、nonatomic、copy、assign、strong、weak等幾個(gè)特性的區(qū)別 這篇文章。
2. 淺復(fù)制與深復(fù)制
對(duì)象的拷貝有淺復(fù)制和深復(fù)制兩種方式。淺復(fù)制只復(fù)制指向?qū)ο蟮闹羔?,并不?fù)制對(duì)象本身;深復(fù)制是直接復(fù)制整個(gè)對(duì)象到另一塊內(nèi)存中。即淺復(fù)制是復(fù)制指針,深復(fù)制是復(fù)制內(nèi)容。
NSObject提供了copy和mutableCopy 方法,copy復(fù)制后對(duì)象是不可變對(duì)象(immutable),mutableCopy復(fù)制后的對(duì)象是可變對(duì)象(mutable),與原始對(duì)象是否可變無(wú)關(guān)。
下面針對(duì)非集合類(lèi)、集合類(lèi)對(duì)象的深復(fù)制、淺復(fù)制進(jìn)行說(shuō)明。
2.1 非集合類(lèi)對(duì)象的copy與mutableCopy
非集合類(lèi)對(duì)象指的是NSString、NSNumber之類(lèi)的對(duì)象,深復(fù)制會(huì)復(fù)制引用對(duì)象的內(nèi)容,而淺復(fù)制只復(fù)制引用這些對(duì)象的指針。因此,如果對(duì)象A被淺復(fù)制到對(duì)象B,對(duì)象B和對(duì)象A引用的是同一內(nèi)存地址的實(shí)例變量或?qū)傩浴?/p>

2.1.1 不可變對(duì)象的copy與mutableCopy
創(chuàng)建一個(gè)Single View Application模板的demo,demo名稱(chēng)為copy&mutableCopy。進(jìn)入ViewController.m,在實(shí)現(xiàn)部分添加以下方法。
// 非容器類(lèi) 不可變對(duì)象
- (void)immutableObject {
// 1.創(chuàng)建一個(gè)string字符串。
NSString *string = @"github.com/pro648";
NSString *stringB = string;
NSString *stringCopy = [string copy];
NSMutableString *stringMutableCopy = [string mutableCopy];
// 2.輸出指針指向的內(nèi)存地址。
NSLog(@"Memory location of string = %p",string);
NSLog(@"Memory location of stringB = %p",stringB);
NSLog(@"Memory location of stringCopy = %p",stringCopy);
NSLog(@"Memory location of stringMutableCopy = %p",stringMutableCopy);
}
上述代碼分步說(shuō)明如下:
- 創(chuàng)建一個(gè)
string字符串,之后通過(guò)賦值,調(diào)用copy、mutableCopy方法進(jìn)行復(fù)制操作。 - 通過(guò)使用
%p,輸出指針?biāo)赶騼?nèi)容的內(nèi)存地址。
然后在viewDidLoad中調(diào)用該方法:
- (void)viewDidLoad {
[super viewDidLoad];
// 1.非容器類(lèi) 不可變對(duì)象
[self immutableObject];
}
運(yùn)行demo,可以看到控制臺(tái)輸出如下:
Memory location of string = 0x1060e6068
Memory location of stringB = 0x1060e6068
Memory location of stringCopy = 0x1060e6068
Memory location of stringMutableCopy = 0x600000072000
可以看到,string、stringB和stringCopy內(nèi)存地址一致,即指向的是同一塊內(nèi)存區(qū)域,進(jìn)行了淺復(fù)制操作。而stringMutableCopy與另外三個(gè)變量?jī)?nèi)存地址不同,系統(tǒng)為其分配了新內(nèi)存,即進(jìn)行了深復(fù)制操作。
2.1.2 可變對(duì)象的copy與mutableCopy
繼續(xù)在實(shí)現(xiàn)部分添加以下方法,并記得在viewDidLoad中調(diào)用。
// 2.非容器類(lèi) 可變對(duì)象
- (void)mutableObject {
// 1.創(chuàng)建一個(gè)可變字符串。
NSMutableString *mString = [NSMutableString stringWithString:@"github.com/pro648"];
NSString *mStringCopy = [mString copy];
NSMutableString *mutablemString = [mString copy];
NSMutableString *mStringMutableCopy = [mString mutableCopy];
// 2.在可變字符串后添加字符串。
[mString appendString:@"AA"];
[mutablemString appendString:@"BB"]; // 運(yùn)行時(shí),這一行會(huì)報(bào)錯(cuò)。
[mStringMutableCopy appendString:@"CC"];
// 3.輸出指針指向的內(nèi)存地址。
NSLog(@"Memory location of \n mString = %p,\n mstringCopy = %p,\n mutablemString = %p,\n mStringMutableCopy = %p",mString, mStringCopy, mutablemString, mStringMutableCopy);
}
在上面代碼中,注釋2部分為可變字符串拼接字符串,運(yùn)行到為mutablemString拼接字符串這一行代碼時(shí),程序會(huì)崩潰,因?yàn)橥ㄟ^(guò)copy方法獲得的字符串是不可變字符串。所以在運(yùn)行前要注釋掉這一行。
運(yùn)行demo,可以看到控制臺(tái)輸出如下:
Memory location of
mString = 0x60000007bc00,
mstringCopy = 0x600000051940,
mutablemString = 0x6000000517c0,
mStringMutableCopy = 0x60000007bec0
可以看到四個(gè)對(duì)象內(nèi)存地址各不相同。所以,這里的copy和mutableCopy執(zhí)行的均為深復(fù)制。
綜合上面兩個(gè)例子,我們可以得出這樣結(jié)論:
- 對(duì)不可變對(duì)象執(zhí)行
copy操作,是指針復(fù)制,執(zhí)行mutableCopy操作是內(nèi)容復(fù)制。 - 對(duì)可變對(duì)象執(zhí)行
copy操作和mutableCopy操作都是內(nèi)容復(fù)制。
用代碼表示如下:
[immutableObject copy]; // 淺復(fù)制
[immutableObject mutableCopy]; // 深復(fù)制
[mutableObject copy]; // 深復(fù)制
[mutableObject mutableCopy] ; // 深復(fù)制
2.2 容器類(lèi)對(duì)象的深復(fù)制、淺復(fù)制
容器類(lèi)對(duì)象指NSArray、NSDictionary等。容器類(lèi)對(duì)象的深復(fù)制、淺復(fù)制如下圖所示:

對(duì)于容器類(lèi),需要探討的是復(fù)制后容器內(nèi)元素的變化,而非容器本身內(nèi)存地址是否發(fā)生了變化。
2.2.1 容器類(lèi)對(duì)象的淺復(fù)制
有許多方法可以對(duì)集合進(jìn)行淺復(fù)制。當(dāng)對(duì)集合進(jìn)行淺復(fù)制時(shí),將復(fù)制原始集合中元素指針到新的集合,即原始集合中元素引用計(jì)數(shù)加一。
<a id="YES">
在實(shí)現(xiàn)部分添加以下方法,并在viewDidLoad中調(diào)用該方法。
// 3.淺復(fù)制容器類(lèi)對(duì)象。
- (void)shallowCopyCollections {
// 1.創(chuàng)建一個(gè)不可變數(shù)組,數(shù)組內(nèi)元素為可變字符串。
NSMutableString *red = [NSMutableString stringWithString:@"Red"];
NSMutableString *green = [NSMutableString stringWithString:@"Green"];
NSMutableString *blue = [NSMutableString stringWithString:@"Blue"];
NSArray *myArray1 = [NSArray arrayWithObjects:red, green, blue, nil];
// 2.進(jìn)行淺復(fù)制。
NSArray *myArray2 = [myArray1 copy];
NSMutableArray *myMutableArray3 = [myArray1 mutableCopy];
NSArray *myArray4 = [[NSArray alloc] initWithArray:myArray1 copyItems:NO];
// 3.修改myArray2的第一個(gè)元素。
NSMutableString *tempString = myArray2.firstObject;
[tempString appendString:@"Color"];
// 4.輸出四個(gè)數(shù)組內(nèi)存地址及四個(gè)數(shù)組內(nèi)容。
NSLog(@"Memory location of \n myArray1 = %p, \n myArray2 %p, \n myMutableArray3 %p, \n myArray4 %p",myArray1, myArray2, myMutableArray3, myArray4);
NSLog(@"Contents of \n myArray1 %@, \n myArray2 %@, \n myMutableArray3 %@, \n myArray4 %@",myArray1, myArray2, myMutableArray3, myArray4);
}
</a>
運(yùn)行demo,可以看到控制臺(tái)輸出如下:
Memory location of
myArray1 = 0x60800004f240,
myArray2 0x60800004f240,
myMutableArray3 0x60800004ef40,
myArray4 0x60800004f090
Contents of
myArray1 (
RedColor,
Green,
Blue
),
myArray2 (
RedColor,
Green,
Blue
),
myMutableArray3 (
RedColor,
Green,
Blue
),
myArray4 (
RedColor,
Green,
Blue
)
可以看到myArray1和myArray2數(shù)組內(nèi)存地址相同,myMutableArray3和myArray4與其它數(shù)組內(nèi)存地址各不相同。這是因?yàn)?code>mutableCopy的對(duì)象會(huì)被分配新的內(nèi)存,alloc會(huì)為對(duì)象分配新的內(nèi)存空間。
觀(guān)察數(shù)組內(nèi)元素,發(fā)現(xiàn)修改myArray2數(shù)組內(nèi)第一個(gè)元素,四個(gè)數(shù)組第一個(gè)元素都發(fā)生了改變,所以這里只進(jìn)行了淺復(fù)制。
2.2.2 容器類(lèi)對(duì)象的深復(fù)制
有兩種方式對(duì)容器類(lèi)對(duì)象進(jìn)行深復(fù)制:
- 第一種方法是:使用
initWithArray: copyItems:類(lèi)型方法,其中,第二個(gè)參數(shù)為YES。 - 第二種方法是:使用歸檔、解檔。
下面先看如何使用initWithArray: copyItems:類(lèi)型方法。使用該方法進(jìn)行深復(fù)制時(shí),第二個(gè)參數(shù)為YES。如果使用該方法對(duì)集合進(jìn)行深復(fù)制,那么集合內(nèi)每個(gè)元素都會(huì)收到copyWithZone: 消息,我們平常使用copy、mutableCopy方法時(shí),系統(tǒng)會(huì)把copy和mutableCopy自動(dòng)替換為copyWithZone:和mutableCopyWithZone:。即copy和mutableCopy只是簡(jiǎn)便方法。如果集合內(nèi)元素遵守NSCopying協(xié)議,元素被復(fù)制到新的集合。如果集合內(nèi)元素不遵守NSCopying協(xié)議,用這樣的方式進(jìn)行深復(fù)制,會(huì)在運(yùn)行時(shí)產(chǎn)生錯(cuò)誤。
copyWithZone: 產(chǎn)生的是淺復(fù)制,所以,這種方法只能產(chǎn)生一層深復(fù)制 one-level-deep copy,如果集合內(nèi)元素仍然是集合,則子集合內(nèi)元素不會(huì)被深復(fù)制,只對(duì)子集合內(nèi)元素指針進(jìn)行復(fù)制。
如果集合內(nèi)元素為不可變對(duì)象,發(fā)送
copyWithZone:消息后進(jìn)行指針復(fù)制,該對(duì)象仍然不可變,因此只進(jìn)行指針復(fù)制即可滿(mǎn)足需求。如果集合內(nèi)元素為可變對(duì)象,發(fā)送
copyWithZone:消息后進(jìn)行的是內(nèi)容復(fù)制,復(fù)制后該元素不可變,此時(shí),完成了一層深復(fù)制。
把上面代碼注釋2部分中,initWithArray: copyItems:第二個(gè)參數(shù)修改為YES,注釋4中輸出部分修改為輸出數(shù)組第一個(gè)元素內(nèi)存地址。更新后如下:
// 4.容器類(lèi)一層深復(fù)制
- (void)oneLevelDeepCopy {
// 1.創(chuàng)建一個(gè)不可變數(shù)組,數(shù)組內(nèi)元素為可變字符串。
NSMutableString *red = [NSMutableString stringWithString:@"Red"];
NSMutableString *green = [NSMutableString stringWithString:@"Green"];
NSMutableString *blue = [NSMutableString stringWithString:@"Blue"];
NSArray *myArray1 = [NSArray arrayWithObjects:red, green, blue, nil];
// 2.進(jìn)行淺復(fù)制。
NSArray *myArray2 = [myArray1 copy];
NSMutableArray *myMutableArray3 = [myArray1 mutableCopy];
NSArray *myArray4 = [[NSArray alloc] initWithArray:myArray1 copyItems:YES];
// 3.修改myArray2的第一個(gè)元素。
NSMutableString *tempString = myArray2.firstObject;
[tempString appendString:@"Color"];
// 4.輸出數(shù)組內(nèi)第一個(gè)元素內(nèi)存地址,輸出四個(gè)數(shù)組。
NSLog(@"Memory location of \n myArray1.firstObject = %p, \n myArray2.firstObject %p, \n myMutableArray3.firstObject %p, \n myArray4.firstObject %p",myArray1.firstObject, myArray2.firstObject, myMutableArray3.firstObject, myArray4.firstObject);
NSLog(@"Contents of \n myArray1 %@, \n myArray2 %@, \n myMutableArray3 %@, \n myArray4 %@",myArray1, myArray2, myMutableArray3, myArray4);
}
運(yùn)行demo,可以看到控制臺(tái)輸出如下:
Memory location of
myArray1.firstObject = 0x600000079980,
myArray2.firstObject 0x600000079980,
myMutableArray3.firstObject 0x600000079980,
myArray4.firstObject 0xa000000006465523
Contents of
myArray1 (
RedColor,
Green,
Blue
),
myArray2 (
RedColor,
Green,
Blue
),
myMutableArray3 (
RedColor,
Green,
Blue
),
myArray4 (
Red,
Green,
Blue
)
可以看到myArray4數(shù)組內(nèi)第一個(gè)元素與其它數(shù)組第一個(gè)元素內(nèi)存地址不同,即進(jìn)行了一層深復(fù)制。
這種對(duì)集合進(jìn)行深復(fù)制的方法,對(duì)其它類(lèi)型集合也有效。如詞典中
initWithDictionary: withItems:方法。
如果你的數(shù)組內(nèi)元素是另一個(gè)數(shù)組,想要進(jìn)行完全深復(fù)制,可以使用歸檔、解歸檔方法。使用該方法時(shí),歸檔對(duì)象要遵守NSCoding協(xié)議。如果你對(duì)歸檔不熟悉,可以查看我的另一篇文章:數(shù)據(jù)存儲(chǔ)之歸檔解檔 NSKeyedArchiver NSKeyedUnarchiver。
下面使用歸檔、解檔的方法進(jìn)行完全深復(fù)制。
// 5.使用歸檔進(jìn)行完全深復(fù)制。
- (void)trueDeepCopy {
// 1.創(chuàng)建一個(gè)可變數(shù)組,數(shù)組第一個(gè)元素是另一個(gè)可變數(shù)組,第二個(gè)元素是另一個(gè)不可變數(shù)組。
NSMutableString *hue = [NSMutableString stringWithString:@"hue"];
NSMutableString *saturation = [NSMutableString stringWithString:@"saturation"];
NSMutableString *brightness = [NSMutableString stringWithString:@"brightness"];
NSMutableArray *hsbArray1 = [NSMutableArray arrayWithObjects:hue, saturation, brightness, nil];
NSArray *hsbArray2 = [NSArray arrayWithObjects:hue, saturation, brightness, nil];
NSMutableArray *hsbArray3 = [NSMutableArray arrayWithObjects:hsbArray1, hsbArray2, nil];
// 2.通過(guò)歸檔、解檔進(jìn)行完全深復(fù)制。
NSData *dataArea = [NSKeyedArchiver archivedDataWithRootObject:hsbArray3];
NSMutableArray *hsbArray4 = [NSKeyedUnarchiver unarchiveObjectWithData:dataArea];
// 3.輸出hsbArray3和hsbArray4數(shù)組第一個(gè)元素內(nèi)存地址。
NSLog(@"Memory location of \n hsbArray3.firstObject = %p, \n hsbArray4.firstObject = %p",hsbArray3.firstObject, hsbArray4.firstObject);
}
上面代碼中,可變數(shù)組hsbArray3第一個(gè)元素是可變數(shù)組hsbArray1,第二個(gè)元素是不可變數(shù)組hsbArray2。
使用歸檔、讀取歸檔方法深復(fù)制后,在控制臺(tái)輸出hsbArray3和hsbArray4第一個(gè)元素內(nèi)存地址。輸出如下:
Memory location of
hsbArray3.firstObject = 0x60000004b100,
hsbArray4.firstObject = 0x60000004b1f0
可以看到hsbArray3和hsbArray4數(shù)組內(nèi)元素內(nèi)存地址不同,即進(jìn)行了一層深復(fù)制。
在trueDeepCopy方法內(nèi),繼續(xù)為hsbArray4數(shù)組內(nèi)第一個(gè)元素tempArray1可變數(shù)組添加字符串對(duì)象。為hsbArray4第二個(gè)元素hsbArray2數(shù)組添加字符串對(duì)象。最后輸出hsbArray3和hsbArray4數(shù)組內(nèi)容。
// 5.使用歸檔進(jìn)行完全深復(fù)制。
- (void)trueDeepCopy {
...
// 4.為hsbArray4第一個(gè)元素添加字符串。
NSMutableArray *tempArray1 = hsbArray4.firstObject;
[tempArray1 addObject:@"hsb"];
// 5.hsbArray4第二個(gè)元素是hsbArray2,而hsbArray2是不可變數(shù)組,這一步將產(chǎn)生錯(cuò)誤。
// NSMutableArray *tempArray2 = hsbArray4[1];
// [tempArray2 addObject:@"Color"];
// 6.輸出數(shù)組內(nèi)容。
NSLog(@"Contents of \n hsbArray3 %@, \n hsbArray4 %@",hsbArray3, hsbArray4);
}
因?yàn)?code>hsbArray4第二個(gè)元素是hsbArray2副本,而hsbArray2是不可變數(shù)組,這一步將產(chǎn)生錯(cuò)誤。注釋掉5部分代碼后,控制臺(tái)輸出如下:
Contents of
hsbArray3 (
(
hue,
saturation,
brightness
),
(
hue,
saturation,
brightness
)
),
hsbArray4 (
(
hue,
saturation,
brightness,
hsb
),
(
hue,
saturation,
brightness
)
)
可以看到只有hsbArray4數(shù)組第一個(gè)元素內(nèi)對(duì)象發(fā)生了改變,所以,使用歸檔、讀取歸檔進(jìn)行的是完全深復(fù)制。
復(fù)制集合時(shí),該集合、集合內(nèi)元素的可變性可能會(huì)受到影響。每種方法對(duì)任意深度集合中對(duì)象的可變性有稍微不同的影響。
-
copyWithZone:創(chuàng)建對(duì)象的最外層 surface level不可變,所有更深層次對(duì)象的可變性不變。 -
mutableCopyWithZone:創(chuàng)建對(duì)象的最外層 surface level可變,所有更深層次對(duì)象的可變性不變。 -
initWithArray: copyItems:第二個(gè)參數(shù)為NO,此時(shí),所創(chuàng)建數(shù)組最外層可變性與初始化的可變性相同,所有更深層級(jí)對(duì)象可變性不變。 -
initWithArray: copyItems:第二個(gè)參數(shù)為YES,此時(shí),所創(chuàng)建數(shù)組最外層可變性與初始化的可變性相同,下一層級(jí)是不可變的,所有更深層級(jí)對(duì)象可變性不變。 - 歸檔、解檔復(fù)制的集合,所有層級(jí)的可變性與原始對(duì)象相同。
2.3 自定義對(duì)象的深復(fù)制、淺復(fù)制
自定義的類(lèi)需要我們自己實(shí)現(xiàn)NSCopying、NSMutableCopying協(xié)議,這樣才可以調(diào)用copy和mutableCopy方法。
添加父類(lèi)為NSObject,名稱(chēng)為Person的類(lèi)。進(jìn)入Person.h,添加以下屬性和方法,同時(shí)讓該類(lèi)遵守NSCopying協(xié)議。
@interface Person : NSObject <NSCopying>
@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) NSUInteger age;
- (void)setName:(NSString *)name withAge:(NSUInteger)age;
@end
NSCopying協(xié)議只有一個(gè)必須實(shí)現(xiàn)的copyWithZone:方法。進(jìn)入Person.m,實(shí)現(xiàn)屬性中setName: withAge:方法和copyWithZone:方法。
- (void)setName:(NSString *)name withAge:(NSUInteger)age {
_name = name;
_age = age;
}
- (id)copyWithZone:(NSZone *)zone {
Person *person = [[Person allocWithZone:zone] init];
[person setName:self.name withAge:self.age];
return person;
}
如果Person類(lèi)會(huì)被繼承,那么copyWithZone:方法將被繼承,這時(shí)應(yīng)將上面的
Person *person = [[Person allocWithZone:zone] init];
替換為
id person = [[[self class] allocWithZone: zone] init];
這樣,可以從該類(lèi)分配一個(gè)新對(duì)象,而這個(gè)類(lèi)是copy的接收者。例如:如果Person類(lèi)有一個(gè)名為NewPerson的子類(lèi),那么應(yīng)該在繼承的方法中分配了新的NewPerson對(duì)象,而不是Person對(duì)象。
如果Person類(lèi)的父類(lèi)也實(shí)現(xiàn)了NSCopying協(xié)議,那么應(yīng)該先調(diào)用父類(lèi)的copy方法,以復(fù)制繼承來(lái)的實(shí)例變量。如果需要實(shí)現(xiàn)可變復(fù)制,還需要遵守NSMutableCopying協(xié)議。
進(jìn)入ViewController.m,導(dǎo)入Person.h,在實(shí)現(xiàn)部分添加以下方法,并在viewDidLoad中調(diào)用。
// 6.自定義類(lèi)的復(fù)制
- (void)copyCustomClass {
Person *person = [[Person alloc] init];
[person setName:@"A" withAge:1];
Person *personCopy = [person copy];
[personCopy setName:@"B" withAge:2];
// 斷點(diǎn)位置
}
在copyCustomClass方法最后一行設(shè)置斷點(diǎn),運(yùn)行demo,可以看到控制臺(tái)輸出如下圖:

通過(guò)上圖可以看到,person和personCopy內(nèi)存地址不同,且person和personCopy中的name和age屬性的值各不相同。
3. 修改指針指向
現(xiàn)在看最后一個(gè)示例,在ViewController.m的實(shí)現(xiàn)部分添加以下方法,并在viewDidLoad中調(diào)用該方法。
// 7.更改指針指向地址
- (void)pointToAnotherMemoryAddress {
// 1.指針a、b同時(shí)指向字符串pro
NSString *a = @"pro";
NSString *b = a;
NSLog(@"Memory location of \n a = %p, \n b = %p", a, b);
// 斷點(diǎn)1位置
// 2.指針a指向字符串pro648
a = @"pro648";
NSLog(@"Memory location of \n a = %p, \n b = %p", a, b);
// 斷點(diǎn)2位置
}
上述代碼分步說(shuō)明如下:
- 指針
a指向字符串pro內(nèi)存地址,b = a表示b是a的淺復(fù)制,指針b也指向字符串pro內(nèi)存地址。NSLog語(yǔ)句可以說(shuō)明這一問(wèn)題。也可以在注釋斷點(diǎn)1位置所在行設(shè)置斷點(diǎn),查看指針指向的內(nèi)容。 - 修改指針
a指向字符串pro648,此時(shí)輸出a、b指針?biāo)傅南騼?nèi)存地址。并在斷點(diǎn)2位置所在行設(shè)置斷點(diǎn)。運(yùn)行后可以看到控制臺(tái)輸出如下:

可以看到,a、b指針指向不同內(nèi)存地址,a指向字符串pro648,b指向字符串pro。
這是因?yàn)?/p>
a = @"pro648";
等同于
a = [[NSString alloc] initWithString:@"pro648"];
a = @"pro648"修改了a指針指向的內(nèi)存地址,而b指針依然指向之前的內(nèi)存地址。
NSString與NSMutableString的區(qū)別主要是:NSMutableString對(duì)象所指向內(nèi)存地址中的內(nèi)容可以被修改,而NSString對(duì)象所指向內(nèi)存地址中內(nèi)容不能被修改,但NSString對(duì)象不是常量,可以通過(guò)為NSString對(duì)象重新分配一塊內(nèi)存來(lái)改變其指向的內(nèi)容。
總結(jié)
淺拷貝盡可能少的復(fù)制對(duì)象,集合的淺拷貝副本只是集合結(jié)構(gòu)的副本,而不是集合內(nèi)元素的副本。淺拷貝獲得的副本與原始集合共享各個(gè)元素。
深拷貝復(fù)制一切內(nèi)容。集合的深拷貝會(huì)復(fù)制集合的結(jié)構(gòu)和元素,但如果集合內(nèi)元素也是集合,則涉及到一層深拷貝、完全深拷貝。
Demo名稱(chēng):copy&mutableCopy
源碼地址:https://github.com/pro648/BasicDemos-iOS
參考資料: