OC中的深複製與淺複製

深淺拷貝的不同


淺拷貝是對對象發出一個retain操作,將指針賦值給新的集合對象,如圖1

淺拷貝

NSArray *shallowCopyArray = [someArray copyWithZone:nil];
 
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];

其中這些方法不侷限於數組,也適用於array對象,其中copy默認調用的是copyWithZone,返回的是不可變的對象,mutalbeCopy默認調用mutalbeCopyWithZone,返回可變對象

深拷貝:

集合方法:initWithDicotonary copyItems:YES是進行深拷貝,它使每個集合內的元素接受copyWithZone消息,其中copyWithZone是淺拷貝,他只進行一層深拷貝

NSArray *deepCopyArray=[[NSArray alloc] initWithArray:someArray copyItems:YES];

如果需要進行純深拷貝(多層次深拷貝),應該用如下方法,需要遵循NSCoding協議:

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
          [NSKeyedArchiver archivedDataWithRootObject:oldArray]];

拷貝和可變性:

拷貝一個集合會對內部元素的可變性有影響,每個方法的影響都稍微不同,具體如下:

  • copyWithZone,返回不可變的首層對象(數組的話默認由消息的發送者進行retainCount+1新的array和舊的array,是首地址的拷貝),所有更深層次的可變性保持不變
  • initWithDictionary: copyItems:NO 返回可變的首層對象,所有更深層次的可變性保持不變
  • initWithDictionary: copyItems:YES返回可變的首層對象,下一層不可變(因爲對每個內部對象發送了一次copyWithZone方法,如果爲實現此方法將會報runtimeError),所有更深層次的可變性保持不變
  • 純深拷貝所有可變性在任意層上保持不變
本文內容是對Apple官方文檔CopyingCollection的翻譯,括號內容是個人理解,如有不準確的地方請留言指出,謝謝
代碼實驗:
//copy 默認調用copyWithZone返回的是不可變的對象,mutableCopy 默認調用mutableCopyWithZone返回可變對象;二者都會對地址進行改變,查看NSObject API有解析
    NSMutableString* im=[[NSMutableString alloc]initWithString:@"Inner_MM"];
    NSString* isx=@"Inner_YYY";
    //測試基本數據類型的copy 裏面默認調用copyWithZone,產生新的副本
    NSString* test=[im copy];
    NSLog(@"im %ld",[im retainCount]);NSLog(@"test %ld",[test retainCount]);
    NSLog(@"im %p",im);NSLog(@"test %p",test);
    [im appendString:@"TEST"];//元數據改變新數據不變
    NSLog(@"im %@",im);NSLog(@"test %@",test);
    NSArray* a=[NSArray arrayWithObjects:im,isx, nil];//集合對象
    //arr1的內容是 a(數組--裏面有可變字符串im和不可變字符串isx) m(可變字符串) s(不可變字符串)
    NSMutableString* m=[[NSMutableString alloc]initWithString:@"MM"];//retainCount1
    NSString* s=@"YYY";
    NSArray* arr1=[NSArray arrayWithObjects:a,m,s, nil];//m retainCount2加入數組加1;s(不可變字符串比較特殊)是不可變的永遠都是-1
    //retainCount是針對內存地址空間的計數器(arr1和arr2地址相同),與具體類別無關--個人理解
    NSLog(@"arr1 %ld",[arr1 retainCount]);
    NSLog(@"arr1 %p",arr1);
    NSArray* arr2=[arr1 copyWithZone:nil];//copyWithZone返回的是消息的接受者
    NSLog(@"copyWithZone a %ld",[a retainCount]);//2未改變
    //arr1 和 arr2的retainCount均爲2,所以是對arr的指針進行了retain(Copy的副本是隱士的被髮送方retain--官方文檔解釋:NSCopying裏面),他的釋放應由發送方來進行釋放
    NSLog(@"copyWithZone arr1 %ld",[arr1 retainCount]);
    NSLog(@"copyWithZone arr2 %ld",[arr2 retainCount]);
    NSLog(@"copyWithZone arr2 %p",arr2);
    //m的引用計數器和地址均相同,因此他們是同一個,所以此處是淺拷貝,但是m的retainCount是2
    NSLog(@"copyWithZone arr1 m %ld",[[arr1 objectAtIndex:1]retainCount]);
    NSLog(@"copyWithZone arr2 m %ld",[[arr2 objectAtIndex:1]retainCount]);
    NSLog(@"copyWithZone arr1 m %p",[arr1 objectAtIndex:1]);
    NSLog(@"copyWithZone arr2 m %p",[arr2 objectAtIndex:1]);
    //initWithItem copyItems:NO 因爲自己alloc地址肯定不一樣,內容一樣,因爲進行了一次淺拷貝,內部數據進行retain+1
    NSArray* arr3=[[NSArray alloc]initWithArray:arr1 copyItems:NO];
    NSLog(@"initWithArray copyItems:NO a %ld",[a retainCount]);//3發送改變
    NSLog(@"initWithArray copyItems:NO arr1 %ld",[arr1 retainCount]);
    NSLog(@"initWithArray copyItems:NO arr3 %ld",[arr3 retainCount]);
    NSLog(@"initWithArray copyItems:NO arr3 %p",arr3);
    [im appendString:@"WW"];
    [m appendString:@"3"];
    NSLog(@"initWithArray copyItems:NO a %@",[arr1 objectAtIndex:0]);
    NSLog(@"initWithArray copyItems:NO a %@",[arr3 objectAtIndex:0]);
    NSLog(@"initWithArray copyItems:NO m %@",[arr1 objectAtIndex:1]);
    NSLog(@"initWithArray copyItems:NO m %@",[arr3 objectAtIndex:1]);
    NSLog(@"initWithArray copyItems:NO m %ld",[[arr1 objectAtIndex:1]retainCount]);//2+1=3
    NSLog(@"initWithArray copyItems:NO m %ld",[[arr3 objectAtIndex:1]retainCount]);
    //initWithItem copyItems:YES 因爲自己alloc地址肯定不一樣,如果是集合對象或自定義對象內容一樣,普通對象不一樣
    NSArray* arr4=[[NSArray alloc]initWithArray:arr1 copyItems:YES];
    NSLog(@"initWithArray copyItems:YES a %ld",[a retainCount]);//4
    NSLog(@"initWithArray copyItems:YES arr1 %ld",[arr1 retainCount]);
    NSLog(@"initWithArray copyItems:YES arr4 %ld",[arr4 retainCount]);
    NSLog(@"initWithArray copyItems:YES arr4 %p",arr4);
    [im appendString:@"FF"];
    [m appendString:@"4"];
    NSLog(@"initWithArray copyItems:YES a %@",[arr1 objectAtIndex:0]);
    NSLog(@"initWithArray copyItems:YES a %@",[arr4 objectAtIndex:0]);
    NSLog(@"initWithArray copyItems:YES m %@",[arr1 objectAtIndex:1]);
    NSLog(@"initWithArray copyItems:YES m %@",[arr4 objectAtIndex:1]);
    NSLog(@"initWithArray copyItems:YES m %ld",[[arr1 objectAtIndex:1]retainCount]);
    NSLog(@"initWithArray copyItems:YES m %ld",[[arr4 objectAtIndex:1]retainCount]);//-1
    NSLog(@"initWithArray copyItems:YES im %ld",[im retainCount]);//此時爲2原因在於加入a的時候+1,此後一直沒變,init不會對數組內數組元素髮消息
    //總結:1,copy裏面默認調用copyWithZone,二者可以認爲基本相同,如果自定義子類繼承父類後有額外的變量,需要重寫此方法,並且首先調用父類方法();2,copyWithZone是對數組指針進行retainCount+1;initWithArray copyItems:NO對數組內部數據進行+1(要區分是對數組+1還是裏面的內容+1);3,initWithArray copyItems:YES,對數組內的每個元素接收一次copyWithZone方法(可查看 NSArray),因此,如果內部元素是數組類型還是淺複製即retainCount+1,而基本類型則是重新Copy一個新副本
    //注:copy產生的是不可變的,所以arr4裏面的index1輸出的retainCount是-1;數組的操作和其他集合類似
    



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章