OC-copy關(guān)鍵字的作用詳解

用途:
1.NSString螃征、NSArray潮罪、NSDictionary 等等經(jīng)常使用copy關(guān)鍵字寒砖,是因?yàn)樗麄冇袑?yīng)的可變類型:NSMutableString谅猾、NSMutableArray柄慰、NSMutableDictionary;
block 也經(jīng)常使用 copy 關(guān)鍵字税娜,具體原因見官方文檔:Objects Use Properties to Keep Track of Blocks
2.block 使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”,在 MRC 中,方法內(nèi)部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的坐搔,但寫上 copy 也無傷大雅,還能時刻提醒我們:編譯器自動對 block 進(jìn)行了 copy 操作敬矩。如果不寫 copy 概行,該類的調(diào)用者有可能會忘記或者根本不知道“編譯器會自動對 block 進(jìn)行了 copy 操作”,他們有可能會在調(diào)用之前自行拷貝屬性值弧岳。這種操作多余而低效凳忙。

下面做下解釋: copy 此特質(zhì)所表達(dá)的所屬關(guān)系與 strong 類似。然而設(shè)置方法并不保留新值禽炬,而是將其“拷貝” (copy)涧卵。 當(dāng)屬性類型為 NSString 時,經(jīng)常用此特質(zhì)來保護(hù)其封裝性腹尖,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個 NSMutableString 類的實(shí)例柳恐。這個類是 NSString 的子類,表示一種可修改其值的字符串热幔,此時若是不拷貝字符串乐设,那么設(shè)置完屬性之后,字符串的值就可能會在對象不知情的情況下遭人更改断凶。所以伤提,這時就要拷貝一份“不可變” (immutable)的字符串,確保對象中的字符串值不會無意間變動认烁。只要實(shí)現(xiàn)屬性所用的對象是“可變的” (mutable)肿男,就應(yīng)該在設(shè)置新屬性值時拷貝一份介汹。

用 @property 聲明 NSString、NSArray舶沛、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字嘹承,是因?yàn)樗麄冇袑?yīng)的可變類型:NSMutableString、NSMutableArray如庭、NSMutableDictionary叹卷,他們之間可能進(jìn)行賦值操作,為確保對象中的字符串值不會無意間變動坪它,應(yīng)該在設(shè)置新屬性值時拷貝一份骤竹。

該問題在下文中也有論述:用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用copy關(guān)鍵字往毡,為什么蒙揣?如果改用strong關(guān)鍵字,可能造成什么問題开瞭?

這個寫法會出什么問題: @property (copy) NSMutableArray *array;

兩個問題:
1懒震、添加,刪除,修改數(shù)組內(nèi)的元素的時候,程序會因?yàn)檎也坏綄?yīng)的方法而崩潰.因?yàn)?copy 就是復(fù)制一個不可變 NSArray 的對象;
2嗤详、使用了 atomic 屬性會嚴(yán)重影響性能 个扰;

第1條的相關(guān)原因在下文中有論述《用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用 copy 關(guān)鍵字葱色,為什么递宅?如果改用strong關(guān)鍵字,可能造成什么問題冬筒?》 以及上文《怎么用 copy 關(guān)鍵字恐锣?》也有論述。
比如下面的代碼就會發(fā)生崩潰

@property (nonatomic, copy) NSMutableArray *mutableArray;
// 下面的代碼就會發(fā)生崩潰
NSMutableArray *array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];

接下來就會奔潰:
-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460

第2條原因舞痰,如下:

該屬性使用了同步鎖,會在創(chuàng)建時生成一些額外的代碼用于幫助編寫多線程程序诀姚,這會帶來性能問題响牛,通過聲明 nonatomic 可以節(jié)省這些雖然很小但是不必要額外開銷。

在默認(rèn)情況下赫段,由編譯器所合成的方法會通過鎖定機(jī)制確保其原子性(atomicity)呀打。如果屬性具備 nonatomic 特質(zhì),則不使用同步鎖糯笙。請注意贬丛,盡管沒有名為“atomic”的特質(zhì)(如果某屬性不具備 nonatomic 特質(zhì),那它就是“原子的”(atomic))给涕。
在iOS開發(fā)中豺憔,你會發(fā)現(xiàn)额获,幾乎所有屬性都聲明為 nonatomic。
一般情況下并不要求屬性必須是“原子的”恭应,因?yàn)檫@并不能保證“線程安全” ( thread safety)抄邀,若要實(shí)現(xiàn)“線程安全”的操作,還需采用更為深層的鎖定機(jī)制才行昼榛。例如境肾,一個線程在連續(xù)多次讀取某屬性值的過程中有別的線程在同時改寫該值,那么即便將屬性聲明為 atomic胆屿,也還是會讀到不同的屬性值奥喻。
因此,開發(fā)iOS程序時一般都會使用 nonatomic 屬性非迹。但是在開發(fā) Mac OS X 程序時环鲤,使用 atomic 屬性通常都不會有性能瓶頸。

如何讓自己的類用 copy 修飾符彻秆?如何重寫帶 copy 關(guān)鍵字的 setter楔绞?

若想令自己所寫的對象具有拷貝功能,則需實(shí)現(xiàn) NSCopying 協(xié)議唇兑。如果自定義的對象分為可變版本與不可變版本酒朵,那么就要同時實(shí)現(xiàn) NSCopying 與 NSMutableCopying 協(xié)議。

具體步驟:
1.需聲明該類遵從 NSCopying 協(xié)議
2.實(shí)現(xiàn) NSCopying 協(xié)議扎附。該協(xié)議只有一個方法:

- (id)copyWithZone:(NSZone *)zone;

注意:一提到讓自己的類用 copy 修飾符蔫耽,我們總是想覆寫copy方法,其實(shí)真正需要實(shí)現(xiàn)的卻是 “copyWithZone” 方法留夜。
e.g.

typedef NS_ENUM(NSInteger, CYLSex) {
 CYLSexMan,
 CYLSexWoman
 };

@interface CYLUser : NSObject<NSCopying> 

@property (nonatomic, readonly, copy) NSString *name; 
@property (nonatomic, readonly, assign) NSUInteger age; 
@property (nonatomic, readonly, assign) CYLSex sex;
 - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
 + (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex; 

@end

然后實(shí)現(xiàn)協(xié)議中規(guī)定的方法:

- (id)copyWithZone:(NSZone *)zone {
   CYLUser *copy = [[[self class] allocWithZone:zone] initWithName:_name age:_age sex:_sex]; 
    return copy;
}

用@property聲明的NSString(或NSArray匙铡,NSDictionary)經(jīng)常使用copy關(guān)鍵字,為什么碍粥?如果改用strong關(guān)鍵字鳖眼,可能造成什么問題?

1.因?yàn)楦割愔羔樋梢灾赶蜃宇悓ο?使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本.
2.如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性.
copy 此特質(zhì)所表達(dá)的所屬關(guān)系與 strong 類似嚼摩。然而設(shè)置方法并不保留新值钦讳,而是將其“拷貝” (copy)。 當(dāng)屬性類型為 NSString 時枕面,經(jīng)常用此特質(zhì)來保護(hù)其封裝性愿卒,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個 NSMutableString 類的實(shí)例。這個類是 NSString 的子類潮秘,表示一種可修改其值的字符串琼开,此時若是不拷貝字符串,那么設(shè)置完屬性之后枕荞,字符串的值就可能會在對象不知情的情況下遭人更改柜候。所以搞动,這時就要拷貝一份“不可變” (immutable)的字符串,確保對象中的字符串值不會無意間變動改橘。只要實(shí)現(xiàn)屬性所用的對象是“可變的” (mutable)滋尉,就應(yīng)該在設(shè)置新屬性值時拷貝一份。
舉例說明:
定義一個以 strong 修飾的 array:

@property (nonatomic ,readwrite, strong) NSArray *array;

然后進(jìn)行下面的操作:

  NSMutableArray *mutableArray = [[NSMutableArray alloc] init]; 
  NSArray *array = @[ @1, @2, @3, @4 ]; 
  self.array = mutableArray;
  [mutableArray removeAllObjects];
  NSLog(@"%@",self.array);

  [mutableArray addObjectsFromArray:array]; 
  self.array = [mutableArray copy]; 
  [mutableArray removeAllObjects];
  NSLog(@"%@",self.array);

打印結(jié)果如下所示:

2015-09-27 19:10:32.523 CYLArrayCopyDmo[10681:713670] (
)
2015-09-27 19:10:32.524 CYLArrayCopyDmo[10681:713670] (
1,
2,
3,
4
)

為了理解這種做法飞主,首先要知道狮惜,兩種情況:
對非集合類對象的 copy 與 mutableCopy 操作;
對集合類對象的 copy 與 mutableCopy 操作碌识。

  1. 對非集合類對象的copy操作:

在非集合類對象中:對 immutable 對象進(jìn)行 copy 操作碾篡,是指針復(fù)制,mutableCopy 操作時內(nèi)容復(fù)制筏餐;對 mutable 對象進(jìn)行 copy 和 mutableCopy 都是內(nèi)容復(fù)制开泽。用代碼簡單表示如下:

[immutableObject copy] // 淺復(fù)制
[immutableObject mutableCopy] //深復(fù)制
[mutableObject copy] //深復(fù)制
[mutableObject mutableCopy] //深復(fù)制

比如以下代碼:

NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copy
NSString *stringCopy = [string copy];

查看內(nèi)存,會發(fā)現(xiàn) string魁瞪、stringCopy 內(nèi)存地址都不一樣穆律,說明此時都是做內(nèi)容拷貝、深拷貝导俘。即使你進(jìn)行如下操作:

[string appendString:@"origion!"]

stringCopy 的值也不會因此改變峦耘,但是如果不使用 copy,stringCopy 的值就會被改變旅薄。 集合類對象以此類推辅髓。 所以,

用 @property 聲明 NSString少梁、NSArray洛口、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,是因?yàn)樗麄冇袑?yīng)的可變類型:NSMutableString凯沪、NSMutableArray第焰、NSMutableDictionary,他們之間可能進(jìn)行賦值操作妨马,為確保對象中的字符串值不會無意間變動樟遣,應(yīng)該在設(shè)置新屬性值時拷貝一份。

2身笤、集合類對象的copy與mutableCopy

集合類對象是指 NSArray、NSDictionary葵陵、NSSet ... 之類的對象液荸。下面先看集合類immutable對象使用 copy 和 mutableCopy 的一個例子:

NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];

查看內(nèi)容,可以看到 copyArray 和 array 的地址是一樣的脱篙,而 mCopyArray 和 array 的地址是不同的娇钱。說明 copy 操作進(jìn)行了指針拷貝伤柄,mutableCopy 進(jìn)行了內(nèi)容拷貝。但需要強(qiáng)調(diào)的是:此處的內(nèi)容拷貝文搂,僅僅是拷貝 array 這個對象适刀,array 集合內(nèi)部的元素仍然是指針拷貝。這和上面的非集合 immutable 對象的拷貝還是挺相似的煤蹭,那么mutable對象的拷貝會不會類似呢笔喉?我們繼續(xù)往下,看 mutable 對象拷貝的例子:

NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArray = [array copy];
NSMutableArray *mCopyArray = [array mutableCopy];

查看內(nèi)存硝皂,如我們所料常挚,copyArray、mCopyArray和 array 的內(nèi)存地址都不一樣稽物,說明 copyArray奄毡、mCopyArray 都對 array 進(jìn)行了內(nèi)容拷貝。同樣贝或,我們可以得出結(jié)論:

在集合類對象中吼过,對 immutable 對象進(jìn)行 copy,是指針復(fù)制咪奖, mutableCopy 是內(nèi)容復(fù)制盗忱;對 mutable 對象進(jìn)行 copy 和 mutableCopy 都是內(nèi)容復(fù)制。但是:集合對象的內(nèi)容復(fù)制僅限于對象本身赡艰,對象元素仍然是指針復(fù)制售淡。用代碼簡單表示如下:

[immutableObject copy] // 淺復(fù)制
[immutableObject mutableCopy] //單層深復(fù)制
[mutableObject copy] //單層深復(fù)制
[mutableObject mutableCopy] //單層深復(fù)制

這個代碼結(jié)論和非集合類的非常相似。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慷垮,一起剝皮案震驚了整個濱河市揖闸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌料身,老刑警劉巖汤纸,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芹血,居然都是意外死亡贮泞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門幔烛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來啃擦,“玉大人,你說我怎么就攤上這事饿悬×铗龋” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長珠叔。 經(jīng)常有香客問我蝎宇,道長,這世上最難降的妖魔是什么祷安? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任姥芥,我火速辦了婚禮,結(jié)果婚禮上汇鞭,老公的妹妹穿的比我還像新娘凉唐。我一直安慰自己琳轿,他們只是感情好毅臊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布劈愚。 她就那樣靜靜地躺著刘急,像睡著了一般携栋。 火紅的嫁衣襯著肌膚如雪列荔。 梳的紋絲不亂的頭發(fā)上浸赫,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天害幅,我揣著相機(jī)與錄音绘沉,去河邊找鬼煎楣。 笑死,一個胖子當(dāng)著我的面吹牛车伞,可吹牛的內(nèi)容都是我干的择懂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼另玖,長吁一口氣:“原來是場噩夢啊……” “哼困曙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谦去,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤慷丽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鳄哭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體要糊,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年妆丘,在試婚紗的時候發(fā)現(xiàn)自己被綠了锄俄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡勺拣,死狀恐怖奶赠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情药有,我是刑警寧澤车柠,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響竹祷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羊苟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一塑陵、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜡励,春花似錦令花、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稽寒,卻和暖如春扮碧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背杏糙。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工慎王, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宏侍。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓赖淤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谅河。 傳聞我的和親對象是個殘疾皇子咱旱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容