用途:
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 操作碌识。
- 對非集合類對象的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é)論和非集合類的非常相似。