這周由于公司招新人跃惫,面試官的一道關(guān)于copy和mutalbeCopy的問題引發(fā)了組員之間的激烈探討叮叹,這時有位號稱selfStrong的同學(xué)跑出了他的必殺技--葵花寶典,說關(guān)于深淺拷貝的問題爆存,看這張表就行了:
看到這張表蛉顽,臥槽,這不就是當(dāng)時教我們的口訣嗎:“copy-指針拷貝-淺拷貝先较,mutableCopy-內(nèi)容拷貝-深拷貝”携冤!,“只有不可變對象調(diào)用copy方法時是淺拷貝拇泣,其他情況都為深拷貝”噪叙。曾經(jīng)一段時間我確實也是靠是否mutable來區(qū)分深淺拷貝的,但經(jīng)過多次和毛毛可的探討和實際的測試霉翔,逐漸發(fā)現(xiàn)這種記憶方式的一些問題。
把拷貝方法稱為copy而非immutableCopy的原因在于苞笨,NSCopying不僅涉及給那些具有可變版本和不可變版本的類來使用债朵,而且還要供其他一些類使用,而那些類沒有“可變”與“不可變”之分瀑凝,所以說序芦,把拷貝方法叫做immutableCopy不合適。 ----Effective OC 2.0
深拷貝/淺拷貝本來就是是兩組不同的概念粤咪,可變/不可變只決定對象的可變性谚中,而拷貝這個概念本意就是產(chǎn)生一個對象的副本,至于是深拷貝還是淺拷貝,用毛毛可不知道從哪找的這句“deep copy copy everything”就能很好的判斷宪塔。因為OC中的copy(immutableCopy)磁奖、mutableCopy方法把可不可變和拷貝這兩種概念聯(lián)系在了一起,所以讓我們覺得是否可變和深淺拷貝兩者之間是一一對應(yīng)的關(guān)系的某筐,但其實想想swift或者別的語言我們肯定不會把var/let和copy/deepCopy聯(lián)系起來吧比搭。
所以單純的說copy就是指針拷貝/淺拷貝或者mutableCopy就是內(nèi)容拷貝/深拷貝只能說在某些情況下是有這種一一對應(yīng)的關(guān)系,但并不能作為一個放之四海而皆準(zhǔn)的判斷規(guī)則南誊,關(guān)于深淺拷貝的問題身诺,我們應(yīng)該按照不同的類型來區(qū)分。
容器類型
可以這樣總結(jié):
類型 | 操作 | 容器 | 內(nèi)容 | 操作返回容器的可變性 | 深淺拷貝 |
---|---|---|---|---|---|
NS* | copy | 舊 | 舊 | 不可 | / |
NS* | mutableCopy | 新 | 舊 | 可 | 淺 |
NSMutable* | copy | 新 | 舊 | 不可 | 淺 |
NSMutable* | mutableCopy | 新 | 舊 | 可 | 淺 |
NS* | initWithXX:copyItems: | 新 | 新 | 不可 | 深 |
NSMutable* | initWithXX:copyItems: | 新 | 新 | 可 | 深 |
容器類默認的copy操作默認都是淺拷貝抄囚,默認都會生成一個新的容器(也就是開辟一塊新的內(nèi)存地址霉赡,至于NS類型返回就容器的問題下面會講到),要想實現(xiàn)容器類的深拷貝幔托,蘋果對于NS及其子類容器類型都提供了一個initWithXX:copyItems:的init方法同廉,給copyItems這個參數(shù)傳YES,該方法生成的新容器中的每個元素都相當(dāng)于對舊容器中相對應(yīng)的元素做了一次copy操作(前提是元素遵守了NSCopying協(xié)議)柑司,這樣新容器對象就是對舊容器對象的一份深拷貝迫肖。
或者利用歸檔和反歸檔技術(shù)來實現(xiàn)深拷貝:
//先將要拷貝的數(shù)組歸檔
NSMutableArray *dataArray = [NSMutableArray array];
NSdata *data = [NSKeyedArchiver archivedDataWithRootObject: dataArray];
//再將歸檔后的數(shù)據(jù)解檔賦值給新的數(shù)組
NSmutableArray *dataArray2 = [NSKeyedArchiver unarchiveOjjectWithData:data];
自定義對象類型:
1.首先遵守NSCopying\NSMutableCopying協(xié)議
2.重寫copyWithZone:/mutableCopyWithZone:方法并返回新對象
- (id)copyWithZone:(NSZone *)zone {
ClassB *copyObject = [[[self class] allocWithZone:zone] init];
copyObject.name = [self.name copy];
return copyObject;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
ClassB *mutableCopyObject = [[[self class] allocWithZone:zone] init];
copyObject.name = [self.name mutableCopy];
return mutableCopyObject
}
王老師提到的關(guān)于自定義對象的mutableCopy的使用場景的問題,我想了想攒驰,如果一個Person有一個NSArray類型的arr屬性蟆湖,他想產(chǎn)生一個能夠帶有mutableArr屬性的Person,那么就可以對這個Person對象進行mutableCopy操作玻粪,這樣copy出來的對象的屬性就都是mutable類型了隅津,比這樣相當(dāng)于對原來的對象進行一種“升級”,目前能想到的也只有這種場景了劲室。
NSString類型
和葵花寶典中的一致:
類型 | 操作 | 內(nèi)存 | 內(nèi)容 | 可變性 | 深淺拷貝 |
---|---|---|---|---|---|
NSSting | copy | 舊 | 舊 | 不可 | / |
NSSting | mutableCopy | 新 | 新 | 可 | 深 |
NSMutableString | copy | 新 | 新 | 不可 | 深 |
NSMutableString | mutableCopy | 新 | 新 | 可 | 淺 |
按理說copy這一操作都應(yīng)該開辟一塊新的內(nèi)存伦仍,至于為啥NSArray/NSString的copy返回的還是舊地址,我們推斷是蘋果做了優(yōu)化很洋,因為對NSArray/NSString這些不可變類型copy后生成的新內(nèi)容和原來是完全一樣的充蓝,如果還去開辟一片新的內(nèi)存地址就造成了浪費,所以對于NS*類型的copy操作喉磁,本質(zhì)上和=的作用一直谓苟,就是個單純的賦值操作,指向原來的對象协怒。
現(xiàn)階段對copy和mutableCopy的理解也就是這樣了涝焙,這樣分開理解感覺更容易理清楚copy/deepCopy和mutable/immutable之間的關(guān)系,有問題可以留言探討孕暇。