iOS--拷貝相關(guān)題

1、什么是深拷貝什么是淺拷貝钻蔑?淺拷貝和深拷貝的區(qū)別

* 淺拷貝(shallow copy):指針拷貝窗怒,對于被拷貝對象的每一層都是指針拷貝球恤,沒有開啟新的內(nèi)存地址堪置,拷貝前后的指針指向同一塊內(nèi)存地址雁竞。淺拷貝會影響內(nèi)存地址引用計數(shù)碑诉。

* 深拷貝(one-level-deep copy):內(nèi)存塊拷貝,拷貝后的指針指向拷貝后的內(nèi)存塊侥锦。但是這里深拷貝只是深拷貝對象自身這一層进栽,是單層深拷貝,對于容器類對象恭垦,容器內(nèi)各層元素對象依然是淺拷貝快毛。


2、copy和strong的區(qū)別

這里分兩種場景番挺,首先對于不可變對象唠帝,

@property(nonatomic, strong)NSString *strongString;

?@property(nonatomic, copy)NSString *cpString;

------------------------------------------------------------------------------------------------------------

? ? NSString *originString = [[NSString alloc] initWithFormat:@"Hello World!"];

? ? self.strongString= originString;

? ? self.cpString= originString;

? ? NSLog(@"? ? ? ? ? ? ? 對象地址? ? ? ? ? 對象指針地址? ? ? 引用計數(shù)");

? ? NSLog(@"originString: %p , %p , %ld", originString, &originString,CFGetRetainCount((__bridge? CFTypeRef)(originString)));

? ? NSLog(@"strongString? : %p , %p , %ld", _strongString, &_strongString, CFGetRetainCount((__bridge? CFTypeRef)(_strongString)));

? ? NSLog(@"cpString? ? : %p , %p , %ld", _cpString, &_cpString, CFGetRetainCount((__bridge? CFTypeRef)(_cpString)));

------打印log-------------------------------------------------------------------------------------------

? ? ? ? ? ? ? ? ? ? ? ? ?對象地址? ? ? ? ? ? ? 對象指針地址? ? ? 引用計數(shù)

originString? ?: 0x600001bcb1a0 , 0x7ffee876c188 , 3

strongString? : 0x600001bcb1a0 , 0x7ff155208ab0 , 3

cpString? ? ? ? : 0x600001bcb1a0 , 0x7ff155208ab8 , 3

對于不可變對象,copy對象和strong對象的內(nèi)存地址都沒有變化玄柏,只是生成新指針指向源對象地址襟衰,引用計數(shù)加 1 , 都是淺拷貝粪摘。

當源對象是可變對象時瀑晒,使用點語法賦值:

@property(nonatomic, strong)NSArray *strongArray;

@property(nonatomic, copy)NSArray *cpArray;

--------------------------------------------------——————————————————

? ? NSLog(@"? ? ? ? ? ? ? 對象地址? ? ? ? ? 對象指針地址? ? ? 引用計數(shù)? ? ? index1元素地址");

? ? NSMutableArray *originArray = [[NSMutableArray alloc] initWithArray:@[@"北京市", @[@"昌平區(qū)", @[@"天通苑"]]]];

? ? self.strongArray= originArray;

? ? self.cpArray= originArray;

? ? NSLog(@"originArray? : %p , %p , %ld, %p", originArray, &originArray,CFGetRetainCount((__bridge? CFTypeRef)(originArray)), originArray[1]);

? ? NSLog(@"strongArray? : %p , %p , %ld, %p", _strongArray, &_strongArray, CFGetRetainCount((__bridge? CFTypeRef)(_strongArray)), _strongArray[1]);

? ? NSLog(@"cpArray? ? ? : %p , %p , %ld, %p", _cpArray, &_cpArray, CFGetRetainCount((__bridge? CFTypeRef)(_cpArray)), _cpArray[1]);

————————————————————————————————————

iOS_copy? ? ? ? 對象地址? ? ? ? ? 對象指針地址? ? ? 引用計數(shù)? ? ? index1元素地址

originArray? : 0x600002414e10 , 0x7ffee12ea180 ,? 2,? ? ? ? ?0x600002a28b80

strongArray : 0x600002414e10 , 0x7fa512307dd0 , 2,? ? ? ? 0x600002a28b80

?cpArray? ? ? : 0x600002a28ae0 , 0x7fa512307dd8 , 1,? ? ? ? ?0x600002a28b80

對可變對象copy绍坝,新指針指向新拷貝開辟的內(nèi)存塊,是深拷貝苔悦,但是內(nèi)部元素是淺拷貝轩褐。strong是新指針指向源內(nèi)存塊,引用計數(shù)加 1间坐,是淺拷貝灾挨。

當源對象是可變對象時,直接賦值:

NSLog(@"? ? ? ? ? ? ? 對象地址? ? ? ? ? 對象指針地址? ? ? 引用計數(shù)? ? ? index1元素地址");

? ? NSMutableArray *originArray = [[NSMutableArray alloc] initWithArray:@[@"北京市", @[@"昌平區(qū)", @[@"天通苑"]]]];

? ? _strongArray= originArray;

? ? _cpArray= originArray;

? ? NSLog(@"originArray? : %p , %p , %ld, %p", originArray, &originArray,CFGetRetainCount((__bridge? CFTypeRef)(originArray)), originArray[1]);

? ? NSLog(@"strongArray? : %p , %p , %ld, %p", _strongArray, &_strongArray, CFGetRetainCount((__bridge? CFTypeRef)(_strongArray)), _strongArray[1]);

? ? NSLog(@"cpArray? ? ? : %p , %p , %ld, %p", _cpArray, &_cpArray, CFGetRetainCount((__bridge? CFTypeRef)(_cpArray)), _cpArray[1]);

——————————————————————————————

? ? ? ? ? ? ? 對象地址? ? ? ? ? 對象指針地址? ? ? 引用計數(shù)? ? ? index1元素地址

originArray? : 0x6000030b91d0 , 0x7ffeed7eb180 , 3, 0x600003e82780

strongArray : 0x6000030b91d0 , 0x7facdf707360 , 3, 0x600003e82780

?cpArray? ? ? : 0x6000030b91d0 , 0x7facdf707368 , 3, 0x600003e82780

看輸出結(jié)果竹宋,直接賦值后對象地址相同劳澄,和上面點語法賦值出現(xiàn)了不同!蜈七!這是為什么呢秒拔?

用@property來聲明屬性變量時,編譯器會自動為我們生成一個以下劃線加屬性名命名的實例變量(@synthesize cpArray = _cpArray)飒硅,并且生成其對應(yīng)的getter砂缩、setter方法。

當我們用self.cpArray = cpArray即使用點語法賦值時三娩,才會調(diào)用cpArray的setter方法庵芭,而_cpArray = originArray 賦值時給_cpArray實例變量直接賦值,并不會調(diào)用cpArray的setter方法雀监,而在setter方法中有一個非常關(guān)鍵的語句:? _cpArray = [cpArray copy];

用點語法self.cpArray = originArray 賦值時双吆,調(diào)用cpArray的setter方法,setter方法對傳入的cpArray做了次深拷貝生成了一個新的對象賦值給_cpArray会前,所以_cpArray指向的地址和對象值都不再和originArray相同好乐。


3、為什么不可變對象要用copy瓦宜,改為strong可以嗎蔚万?

不可變對象使用copy修飾可以讓該對象不受傳入對象的影響。外界傳入的是一個不可變對象時和strong修飾差別不大临庇;當傳入的是一個可變對象時會進行深拷貝反璃,屬性指針指向新的地址,保證該對象持有的是一個不可變副本假夺。

使用strong修飾時淮蜈,如果這個屬性指向一個可變對象,修改可變對象時侄泽,這個屬性值也會被修改。因為在賦值時蜻韭,直接將屬性的指針指向了可變對象地址悼尾,內(nèi)存地址共用一個柿扣。當然我們可以對一個strong對象使用 strongObj = [originObj copy] 來實現(xiàn)copy修飾作用,但是這樣并不保險闺魏,還是直接用copy修飾不可變對象為好未状。


4、數(shù)組copy后里面的元素會復(fù)制一份新的嗎

數(shù)組copy后里面的元素不會復(fù)制一份新的析桥。無論是copy還是mutableCopy司草,數(shù)組內(nèi)的元素都不會不會復(fù)制一份新的,都是淺拷貝泡仗。

使用- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag函數(shù)埋虹,flag為YES時可以對數(shù)組元素進行深拷貝,復(fù)制一份新的娩怎。

使用歸檔解檔的方法(NSArray *deepCpArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:originArray]];)實現(xiàn)了對數(shù)組的完全深拷貝搔课,數(shù)組的每層元素都會復(fù)制一份新的。需要注意的是截亦,歸檔解檔數(shù)組內(nèi)的元素需要實現(xiàn)NSCoding協(xié)議爬泥。

5、[object copy]是淺拷貝還是深拷貝崩瓤?為什么是淺拷貝袍啡?copy是實現(xiàn)了哪個協(xié)議?

[object copy]却桶,object是不可變對象境输,是指針拷貝,新的指針指向源對象內(nèi)存肾扰,是淺拷貝畴嘶;object是可變對象,對源對象內(nèi)存拷貝集晚,新指針指向新地址窗悯,實現(xiàn)的深拷貝,但是這個深拷貝是單層深拷貝偷拔,因為object是容器類對象時蒋院,容器內(nèi)元素還是淺拷貝。

copy方法實際上是調(diào)用NSCopying協(xié)議莲绰。如果沒有實現(xiàn)copyWithZone方法會拋出異常欺旧。NSObject本身并沒有支持NSCopying協(xié)議,子類必須支持此協(xié)議并且實現(xiàn)copyWithZone方法蛤签。子類在實現(xiàn)copyWithZone方法時應(yīng)該先調(diào)用欺負類的copyWithZone方法以讓內(nèi)容復(fù)制完全辞友,除非此類直接繼承自NSObject

示例,Animal類,Dog類繼承自Animal称龙。首先需要遵守NSCopying協(xié)議留拾,下面是copyWithZone實現(xiàn)。

@implementation Animal

- (id)copyWithZone:(NSZone*)zone

{

? ? /*若該類直接繼承自NSObject鲫尊,則在copyWithZone:中使用allocWithZone:初始化*/

? ? /*使用 [self class] 而非Animal是因為此方法可能由子類super調(diào)用過來的*/

? ? Animal*animal = [[[selfclass]allocWithZone:zone]init];

? ? animal.weight=self.weight;

? ? returnanimal;

}

@implementation Dog

- (id)copyWithZone:(NSZone*)zone

{

? ? /*一個子類調(diào)用copy痴柔,實際上是調(diào)用了NSCopying協(xié)議中的copyWithZone:方法

?? ? 調(diào)用super的copyWithZone:確保父類的內(nèi)容被拷貝*/

? ? Dog*dog = [supercopyWithZone:zone];

? ? dog.name=self.name;

? ? returndog;

}

我們demo調(diào)用copy,輸出對象地址和引用計數(shù)

Dog*dog = [[Dogalloc]init];

? ? dog.name=@"Dahuang";

? ? dog.weight=@"11K";

? ? Dog*dog2 = [dogcopy];

? ? NSLog(@"%p,,,,,%p", dog, dog2);

? ? NSLog(@"%ld, %ld", CFGetRetainCount((__bridge? CFTypeRef)(dog)), CFGetRetainCount((__bridge? CFTypeRef)(dog2)));

-----------------------輸出-------log-------------------------------------

iOS_copy[50970:4411255] 0x600000a80f00,,,,,0x600000a80ec0

2020-07-09 11K2020-07-09 15:55:52.340368+0800 iOS_copy[50970:4411255] 1, 1

我們看到copy實現(xiàn)的是深拷貝疫向,當我們將Dog類中的copyWithZone中直接return self:

@implementation Dog

- (id)copyWithZone:(NSZone*)zone

{

//直接 return self咳蔚;? 實現(xiàn)的是淺拷貝

? ? return self;

? ? /*一個子類調(diào)用copy,實際上是調(diào)用了NSCopying協(xié)議中的copyWithZone:方法

?? ? 調(diào)用super的copyWithZone:確保父類的內(nèi)容被拷貝*/

? ? /*Dog*dog = [supercopyWithZone:zone];

? ? dog.name=self.name;

? ? returndog;*/

}

再次運行調(diào)用demo搔驼,輸出

2020-07-09 16:02:37.533212+0800 iOS_copy[51188:4415464] 0x6000026f1120,,,,,0x6000026f1120

2020-07-09 16:02:37.533417+0800 iOS_copy[51188:4415464] 2, 2

此時地址copy實現(xiàn)的是淺拷貝

6谈火、對可變對象進行copy是深拷貝還是淺拷貝?

對系統(tǒng)可變對象進行copy是單層深拷貝匙奴,返回一個不可變對象堆巧。當這個對象是非容器類對象時copy也可以叫做完全深拷貝。當這個對象是容器類對象時copy是單層深拷貝泼菌,這里單層是指對對象本身是深拷貝(內(nèi)存塊拷貝)谍肤,對象內(nèi)的元素是淺拷貝(指針拷貝)。

7哗伯、@property (copy) NSMutableArray *array; 這種寫法有什么問題荒揣?

1、雖然用了NSMutableArray聲明焊刹,但是copy修飾返回的是不可變數(shù)組系任,在對array的元素進行增,刪虐块,改時俩滥,程序會因為找不到對應(yīng)的方法而崩潰。

2贺奠、關(guān)于ARC下霜旧,不顯式指定屬性關(guān)鍵字時,默認關(guān)鍵字入下:

? ? ? ?1).基本數(shù)據(jù)類型:atomic readwrite assign

? ? ? ?2).普通OC對象: atomic readwrite strong

?所以這里默認使用了 atomic 屬性會嚴重影響性能儡率。


8挂据、什么是單層拷貝,怎么實現(xiàn)多層拷貝儿普?

這里可以先看一下前面一篇文章iOS--拷貝 - 簡書崎逃。

這里需要提到容器類對象,系統(tǒng)的容器類對象如NSArray眉孩,NSDictionary等个绍。

在對不可變NSArray做copy操作后勒葱,是拷貝一個新的指針指向NSArray的地址,單純的指針拷貝巴柿,是淺拷貝错森。

對可變數(shù)組NSMutableArray做copy操作后,是對源對象內(nèi)存塊拷貝篮洁,新指針指向新地址,但是對象內(nèi)的元素只是做指針拷貝的淺拷貝殃姓,此時稱為單層拷貝袁波。

NSArray有一個- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag函數(shù),flag為YES時蜗侈,副本對象首層元素也做了內(nèi)存塊拷貝篷牌,但是源對象第二層元素(首層元素是容器類元素的,首層元素內(nèi)的元素)以及更深層的元素依然是淺拷貝踏幻,源對象自身加上首層原色的深拷貝枷颊,這個函數(shù)實現(xiàn)了雙層深拷貝。

使用歸檔解檔的方法(NSArray *deepCpArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:originArray]];)實現(xiàn)了容器類對象和對象內(nèi)的每層元素的深拷貝该面。需要注意的是夭苗,歸檔解檔數(shù)組內(nèi)的元素需要實現(xiàn)NSCoding協(xié)議。

9隔缀、修飾block屬性時题造,為什么用copy?

對于這個問題猾瘸,得區(qū)分 MRC 環(huán)境 和 ARC 環(huán)境界赔;block使用copy是從MRC遺留下來的,在MRC中,方法內(nèi)部的block是在棧區(qū)的,對于分配在棧區(qū)的對象牵触,我們很容易會在釋放之后繼續(xù)調(diào)用淮悼,導致程序奔潰,所以我們使用的時候需要將棧區(qū)的對象移到堆區(qū)揽思,來延長該對象的生命周期袜腥。

對于 MRC 環(huán)境,使用 Copy 修飾 Block绰更,會將棧區(qū)的 Block 拷貝到堆區(qū)瞧挤。

對于 ARC 環(huán)境,使用 Strong儡湾、Copy 修飾 Block特恬,都會將棧區(qū)的 Block 拷貝到堆區(qū)。

所以徐钠,Block 不是一定要用 Copy 來修飾的癌刽,在 ARC 環(huán)境下面 Strong 和 Copy 修飾效果是一樣的。


10、有屬性聲明?@property(nonatomic, copy)NSString *obj;? ?重寫setter方法如下:

- (void)setObj:(NSString*)obj

{

? ? _obj= obj;

}? ? ? ?有什么問題嗎显拜?

有沒有問題衡奥?直接上代碼

@interface ViewController ()

@property(nonatomic, copy)NSString *obj;

@end

@implementation ViewController

- (void)viewDidLoad {

? ? [super viewDidLoad];

? ? NSMutableString *str = [[NSMutableString alloc] initWithFormat:@"Hello World!"];

? ? self.obj= str;

//將一個可變字符串 str 賦值給了 obj,然后改變 str远荠,打印 str 和 obj

? ? [str insertString:@" I am coming!" atIndex: str.length];

? ? NSLog(@"\nstr:%@\nobj:%@", str, self.obj);

}

- (void)setObj:(NSString*)obj

{

? ? _obj= obj;

}

看一下輸出:

2020-07-10 10:41:53.266417+0800 iOS_property[83857:5014465]?

str:Hello World! I am coming!

obj:Hello World! I am coming!

我們將一個可變字符串 str 賦值給了 obj矮固,然后改變 str,obj 居然 跟著 str 一樣被改變了譬淳,雖然我們聲明中使用了copy 修飾5抵贰!邻梆!

問題就在setObj中 我們使用了? _obj= obj;? 直接賦值守伸,直接賦值和strong沒有區(qū)別,對于使用copy修飾的屬性浦妄,在重寫setter方法時尼摹,應(yīng)該使用_obj= [obj copy];??賦值

構(gòu)造方法也一樣,需要用傳入copy后的字符串賦值

如上面的例子,如果僅用 obj 來構(gòu)造控制器ViewController,且寫成這樣:

- (instancetype)initWithObj:(NSString*)obj

{

? ? if(self= [superinit]) {

? ? ? ? //_obj= [obj? copy];? ?//應(yīng)該講copy后的值 賦值給obj

? ? ? ? _obj = obj;? ?//這樣寫是有問題的

? ? }

? ? return self;

}

這樣寫也是有問題的剂娄,如果構(gòu)造的時候傳入的是個可變字符串蠢涝,且在構(gòu)造完后立馬修改它,屬性也會跟著改變阅懦。因為這里賦值 obj 屬性用的是屬性的實例變量_obj惠赫,沒有調(diào)用set方法,所以必須把賦值改為_obj= [obj? copy]; 故黑。這里如果寫成self.obj = obj走set方法來賦值屬性本身是可以避免這種情況儿咱,但不建議,構(gòu)造方法一般還是直接操作屬性的實例變量_obj合適场晶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末混埠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子诗轻,更是在濱河造成了極大的恐慌钳宪,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扳炬,死亡現(xiàn)場離奇詭異吏颖,居然都是意外死亡,警方通過查閱死者的電腦和手機恨樟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門半醉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劝术,你說我怎么就攤上這事缩多〈艮龋” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵衬吆,是天一觀的道長梁钾。 經(jīng)常有香客問我,道長逊抡,這世上最難降的妖魔是什么姆泻? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮冒嫡,結(jié)果婚禮上麦射,老公的妹妹穿的比我還像新娘。我一直安慰自己灯谣,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布蛔琅。 她就那樣靜靜地躺著胎许,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罗售。 梳的紋絲不亂的頭發(fā)上辜窑,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音寨躁,去河邊找鬼穆碎。 笑死,一個胖子當著我的面吹牛职恳,可吹牛的內(nèi)容都是我干的所禀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼放钦,長吁一口氣:“原來是場噩夢啊……” “哼色徘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起操禀,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤褂策,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后颓屑,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斤寂,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年揪惦,在試婚紗的時候發(fā)現(xiàn)自己被綠了遍搞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡器腋,死狀恐怖尾抑,靈堂內(nèi)的尸體忽然破棺而出歇父,到底是詐尸還是另有隱情,我是刑警寧澤再愈,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布榜苫,位于F島的核電站,受9級特大地震影響翎冲,放射性物質(zhì)發(fā)生泄漏垂睬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一抗悍、第九天 我趴在偏房一處隱蔽的房頂上張望驹饺。 院中可真熱鬧,春花似錦缴渊、人聲如沸赏壹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝌借。三九已至,卻和暖如春指蚁,著一層夾襖步出監(jiān)牢的瞬間菩佑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工凝化, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稍坯,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓搓劫,卻偏偏與公主長得像瞧哟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子枪向,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355