Copy是創建壹個新對象,retain是創建壹個指針,引用對象計數增加1。Copy屬性表示兩個對象的內容相同,新對象retain為1,與舊對象的引用計數無關,舊對象沒有變化。復制減少了對象對上下文的依賴。
retain屬性表示兩個對象具有相同的地址(創建壹個指針並復制該指針),內容當然是相同的。該對象的retain值為+1,即retain是指針副本,而副本是內容副本。
當然,在ios中,並不是所有的對象都支持copy和mutablecopy。符合NSCopying協議的類可以發送Copy消息,符合NSMutableCopying協議的類可以發送mutableCopy消息。如果發送的副本或可變副本不符合兩個上訴協議,則會出現異常。但是,默認的ios類不符合這兩種協議。如果要自定義copy,則必須遵守NSCopying並實現copyWithZone:方法。如果要自定義mutableCopy,則必須遵守mutableCopy並實現mutableCopyWithZone:方法。
首先,我們需要有這樣壹個前提:
【array add object:obj】;
這樣,obj的引用計數將增加1,如果使用remove,obj的引用計數將減少1。
這就是ios在內存中處理集合的方式。
因此,假設obj僅由array擁有:
id temp =【array objectAtIndex:0】;
【array removeObjectAtIndex:0】;
如果妳想再次使用temp,妳會得到壹個錯誤,因為此時obj已經被釋放了。
(提醒壹下,如果您使用NSString進行測試,請註意@“ABC”是壹個常量:-))
因為集合類的值經常在程序中傳遞,所以簡單的retain可能不夠,還需要集合內容的副本,即深度副本。
下面來討論壹下。
Ios提供了copy和mutablecopy方法。顧名思義,copy是復制壹個imutable對象,而mutablecopy是復制壹個mutable對象。下面將舉幾個例子來說明。
1.系統的非容器類對象
這指的是NSString、NSNumber等對象。
ns string * string = @“origin“;
ns string * string copy =【string copy】;
NSMutableString * string copy =【string mutable copy】;
【string copy append string:@“!!"];
查看內存,我們可以發現string和stringCopy指向同壹個內存區域(也稱為apple弱引用)。此時,stringCopy和string的引用計數都是2。StringMCopy就是我們所說的真正的副本,系統會為它分配新的內存,但指針指向的字符串仍然與string指向的字符串相同。
請看下面的例子:
NSMutableString * string =【NSMutableString string withstring:@“origin“】;
ns string * string copy =【string copy】;
NSMutableString * mStringCopy =【string copy】;
NSMutableString * string copy =【string mutable copy】;
【mStringCopy appendString:@“mm“】;//錯誤
【字符串附錄字符串:@“origin!“];
【string copy append string:@“!!"];
以上四個NSString對象分配的內存是不同的。但是對於mStringCopy來說,它實際上是壹個imutable對象,所以上面會報錯。
對於系統的非容器類對象,我們可以認為如果我們復制壹個不可變的對象,copy是指針復制(淺層復制),mutableCopy是對象復制(深層復制)。如果復制可變對象,它們都是深度副本,但復制返回的對象是不可變的。
2.系統的容器類對象
指NSArray、NSDictionary等。對於容器類本身,上面討論的結論也適用,需要討論的是復制後容器中對象的變化。
//copy返回不可變對象,mutablecopy返回可變對象。
NSArray * array 1 =【NSArray arrayWithObjects:@“a“,@“b“,@“c“,nil】;
NSArray * array copy 1 =【array 1 copy】;
//arrayCopy1與array是同壹個NSarray對象(指向同壹個對象),array中的元素也指向同壹個指針。
NSLog(@“array 1保留計數:%d“,【array 1 retain count】);
NSLog(@“array 1保留計數:%d“,【array copy 1 retain count】);
NSMutableArray * marray copy 1 =【array 1 mutable copy】;
//ArrayCopy 1是array1的變量副本,它指向與array1不同的對象,但其元素指向與array1中相同的對象。Maraycopy 1還可以修改自己的對象。
【marray copy 1 add object:@“de“】;
【marray copy 1 removeObjectAtIndex:0】;
Array1和arrayCopy1是指針副本,而MaryCopy 1是對象副本,MaryCopy 1還可以在句點:delete或add中更改元素。但是請註意,容器中元素的內容都是指針副本。
讓我們用另壹個例子來測試它。
NSArray * marray 1 =【NSArray arrayWithObjects:【NSMutableString string withstring:@“a“】,@“b“,@“c“,nil】;
NSArray * marray copy 2 =【marray 1 copy】;
NSLog(@“mArray1保留計數:%d“,【marray 1保留計數】);
NSMutableArray * marraymcopy 1 =【marray 1 mutable copy】;
NSLog(@“mArray1保留計數:%d“,【marray 1保留計數】);
//ArrayCopy2、MarryMcCoy1和Marry1都指向不同的對象,但其中的元素是同壹個對象-同壹指針。
//做壹次測試。
NSMutableString * testString =【marray 1 objectAtIndex:0】;
//testString = @“1a 1“;//這將更改testString的指針,並實際將@“1a 1“臨時對象分配給testString。
【testString appendString:@“tail“】;//這樣,上面三個數組的第壹個元素就被更改了。
因此,對於容器來說,它的元素對象總是指針副本。如果您也需要復制元素對象,則需要實現深度復制。/library/MAC/# documentation/Cocoa/Conceptual/Collections/Articles/copying . html
NSArray * array =【NSArray arrayWithObjects:【NSMutableString string withstring:@“first“】,【NSStringstringWithString:@“b“】,@“c“,nil】;
NSArray * deepcopyray =【【NSArray alloc】init with array:array copy items:YES】;
NSArray * truedepcopyarray =【nskeydunarchiver unarchiveObjectWithData:
【nskeydarchiver archivedDataWithRootObject:array】】;
TrueDeepCopyArray是完全意義上的深度副本,但DeepCopyArray不是。對於deepCopyArray中的不可變元素,它仍然是指針副本。或者我們可以自己實現深度復制。因為如果容器的壹個元素是不可變的,那麽對象在妳復制它之後就不能被改變,所以妳只需要用壹個指針復制它。除非您重新分配容器中的元素,否則指針復制就足夠了。例如,在【【ArrayObjectIndex:0】AppendString:@“SD“】之後,容器中的其他對象將不受影響。【【ArrayObjectIndex: 1】和【【Deepcopy ArrayObjectIndex: 0】指向同壹個內存,但我們不能修改它——因為它是不可變的。所以指針復制就夠了。所以這並不是完全意義上的深度復制,但蘋果官方文檔將其列為深度復制,並添加了對復制和可變性之間關系的描述,因此我將在此進行解釋。