[Objective-C]Copy of Custom class

寫這篇的前因後果Java is 'pass by value'
這篇不談pass by reference、pass by value,或是Objective-C下的深拷貝、淺拷貝,網路上已經不少文章可參考,這邊直接透過最常見的ViewController傳值來演示會遇到的問題與解決辦法。

情境
●有二個ViewController:MainViewController、DetailViewController
●MainViewController有一個mutable的origionalArray,用途是呈現資料於tableview
●DetailViewController也有一個mutable的copyArray,用途是編輯資料於tableview,且資料是複製origionalArray來的
●當DetailViewController編輯結束後,用戶可以選擇"Save / Cancel"決定是否保留這次的編輯資料並回到上一頁MainViewController。若是,則copyArray會被用來更新origionalArray;若否,則不影響origionalArray

問題
●不管用戶選擇"Save / Cancel",origionalArray都會被copyArray影響

解法
●檢查MainViewController傳值的方式
DetailViewController *vc = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
// 寫法1
vc.copyArray= self.origionalArray;
// 寫法2
vc.copyArray= [self.origionalArray mutableCopy];
// 寫法3
vc.copyArray = [[NSMutableArray alloc] initWithArray:self.origionalArray copyItems:YES];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vc];
[self presentViewController:navCon animated:YES completion:nil];

寫法1或寫法2屬於淺拷貝,copyArray都沒有獨立於origionalArray,更新copyArray的同時也會更新origionalArray
這時候嘗試寫法3進行深拷貝,竟會報錯-[ManagementResponser copyWithZone:]: unrecognized selector sent to instance xxxxx 。那是因為origionalArray、copyArray所存放之資料為 'ManagementResponser' 自訂型別,要解決問題必須在custom class實作NSCopying,如何實作請看下圖

實作完成後,寫法3就能正常運行了。

參考資料
Pass-by-Value and Pass-by-Reference in Objective-C: A Refactoring Heuristic
--Copy Array相關
NSMutableCopying
mutable copy copies by reference, not value?
Objective-C: Copy Array, Modify Element (Object) in the Copy ONLY
NSMutableArray of custom classes copy
--NSCopying相關
Copying Objects in Objective-C
iOS OC NSCopying 协议
Objective-c的NSCopying协议

1 則留言:

piggy 提到...

補充一點,如果custom class層級很深,例如A Class裡面又包B Class,且二者都為custom class,那麼A、B就都一定要實作NSMutableCopying,不然又會變成淺拷貝~使得變數互相影響!