1.什么情況使用關(guān)鍵字copy? 相比 assign ?有什么不同晶衷?
(1).在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候阴孟,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性税迷。
(2).自身已經(jīng)對它進(jìn)行一次強(qiáng)引用,沒有必要再強(qiáng)引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak永丝;當(dāng)然,也可以使用strong箭养。在下文也有論述:《IBOutlet連出來的視圖屬性為什么可以被設(shè)置成weak?》
不同點:
(1).weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)慕嚷。為這種屬性設(shè)置新值時,設(shè)置方法既不保留新值毕泌,也不釋放舊值喝检。此特質(zhì)同assign類似, 然而在屬性所指的對象遭到摧毀時撼泛,屬性值也會清空(nil out)挠说。 而 assign 的“設(shè)置方法”只會執(zhí)行針對“純量類型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作愿题。
(2).assigin 可以用非 OC 對象,而 weak 必須用于 OC 對象损俭。
2.怎么用copy關(guān)鍵字?
用途:
(1).NSString潘酗、NSArray杆兵、NSDictionary 等等經(jīng)常使用copy關(guān)鍵字,是因為他們有對應(yīng)的可變類型:NSMutableString仔夺、NSMutableArray琐脏、NSMutableDictionary;
(2).block 也經(jīng)常使用 copy 關(guān)鍵字缸兔,具體原因見官方文檔:Objects Use Properties to Keep Track of Blocks:
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)用之前自行拷貝屬性值精算。這種操作多余而低效。你也許會感覺我這種做法有些怪異碎连,不需要寫依然寫灰羽。如果你這樣想,其實是你“日用而不知”鱼辙,你平時開發(fā)中是經(jīng)常在用我說的這種做法的廉嚼,比如下面的屬性不寫copy也行,但是你會選擇寫還是不寫呢倒戏?
@property (nonatomic, copy) NSString *userId;
- (instancetype)initWithUserId:(NSString *)userId {
self = [super init];
if (!self) {
return nil;}
_userId = [userId copy];
return self;
}
下面做下解釋: copy 此特質(zhì)所表達(dá)的所屬關(guān)系與 strong 類似怠噪。然而設(shè)置方法并不保留新值,而是將其“拷貝” (copy)杜跷。 當(dāng)屬性類型為 NSString 時傍念,經(jīng)常用此特質(zhì)來保護(hù)其封裝性,因為傳遞給設(shè)置方法的新值有可能指向一個 NSMutableString 類的實例葛闷。這個類是 NSString 的子類憋槐,表示一種可修改其值的字符串,此時若是不拷貝字符串淑趾,那么設(shè)置完屬性之后阳仔,字符串的值就可能會在對象不知情的情況下遭人更改。所以治笨,這時就要拷貝一份“不可變” (immutable)的字符串驳概,確保對象中的字符串值不會無意間變動。只要實現(xiàn)屬性所用的對象是“可變的” (mutable)旷赖,就應(yīng)該在設(shè)置新屬性值時拷貝一份顺又。
用 @property 聲明 NSString、NSArray等孵、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字稚照,是因為他們有對應(yīng)的可變類型:NSMutableString、NSMutableArray俯萌、NSMutableDictionary果录,他們之間可能進(jìn)行賦值操作,為確保對象中的字符串值不會無意間變動咐熙,應(yīng)該在設(shè)置新屬性值時拷貝一份弱恒。
3. 這個寫法會出什么問題: @property (copy) NSMutableArray *array;
兩個問題:1、添加棋恼,刪除返弹,修改數(shù)組內(nèi)的元素的時候锈玉,程序會因為找不到對應(yīng)的方法而崩潰。因為 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ā)生崩潰:
//.h
@property (nonatomic, copy) NSMutableArray *mutableArray;
//.m
NSMutableArray *array = [NSMutableArray arrayWithObjects:@1,@2,nil];
self.mutableArray = array;
[self.mutableArray removeObjectAtIndex:0];
接下來就會發(fā)生崩潰:
-[__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耸袜。
一般情況下并不要求屬性必須是“原子的”,因為這并不能保證“線程安全” ( thread safety)牲平,若要實現(xiàn)“線程安全”的操作堤框,還需采用更為深層的鎖定機(jī)制才行。例如纵柿,一個線程在連續(xù)多次讀取某屬性值的過程中有別的線程在同時改寫該值蜈抓,那么即便將屬性聲明為 atomic,也還是會讀到不同的屬性值昂儒。
因此沟使,開發(fā)iOS程序時一般都會使用 nonatomic 屬性。但是在開發(fā) Mac OS X 程序時渊跋,使用 atomic 屬性通常都不會有性能瓶頸腊嗡。
4. @property 的本質(zhì)是什么着倾?ivar、getter叽唱、setter 是如何生成并添加到這個類中的
@property 的本質(zhì)是什么屈呕?
@property = ivar + getter + setter;
下面解釋下:
“屬性” (property)有兩大概念:ivar(實例變量)、存取方法(access method = getter + setter)棺亭。
“屬性” (property)作為 Objective-C 的一項特性虎眨,主要的作用就在于封裝對象中的數(shù)據(jù)。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量镶摘。實例變量一般通過“存取方法”(access method)來訪問嗽桩。其中,“獲取方法” (getter)用于讀取變量值凄敢,而“設(shè)置方法” (setter)用于寫入變量值碌冶。這個概念已經(jīng)定型,并且經(jīng)由“屬性”這一特性而成為 Objective-C 2.0 的一部分涝缝。 而在正規(guī)的 Objective-C 編碼風(fēng)格中扑庞,存取方法有著嚴(yán)格的命名規(guī)范。 正因為有了這種嚴(yán)格的命名規(guī)范拒逮,所以 Objective-C 這門語言才能根據(jù)名稱自動創(chuàng)建出存取方法罐氨。其實也可以把屬性當(dāng)做一種關(guān)鍵字,其表示:
編譯器會自動寫出一套存取方法滩援,用以訪問給定類型中具有給定名稱的變量栅隐。 所以你也可以這么說:
@property = getter + setter;
例如下面這個類:
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end
上述代碼寫出來的類與下面這種寫法等效:
@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end
ivar、getter玩徊、setter 是如何生成并添加到這個類中的?
“自動合成”( autosynthesis)
完成屬性定義后租悄,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”(autosynthesis)恩袱。需要強(qiáng)調(diào)的是泣棋,這個過程由編譯 器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”(synthesized method)的源代碼憎蛤。除了生成方法代碼 getter外傅、setter 之外,編譯器還要自動向類中添加適當(dāng)類型的實例變量俩檬,并且在屬性名前面加下劃線萎胰,以此作為實例變量的名字。在前例中棚辽,會生成兩個實例變量技竟,其名稱分別為 _firstName 與 _lastName。也可以在類的實現(xiàn)代碼里通過 @synthesize 語法來指定實例變量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
屬性實現(xiàn)過程中大致生成了五個東西:
OBJC_IVAR_$類名$屬性名稱 :該屬性的“偏移量” (offset)屈藐,這個偏移量是“硬編碼” (hardcode)榔组,表示該變量距離存放對象的內(nèi)存區(qū)域的起始地址有多遠(yuǎn)熙尉。
setter 與 getter 方法對應(yīng)的實現(xiàn)函數(shù)
ivar_list :成員變量列表
method_list :方法列表
prop_list :屬性列表
也就是說我們每次在增加一個屬性,系統(tǒng)都會在 ivar_list 中添加一個成員變量的描述搓扯,在 method_list 中增加 setter 與 getter 方法的描述检痰,在屬性列表中增加一個屬性的描述,然后計算該屬性在對象中的偏移量锨推,然后給出 setter 與 getter 方法對應(yīng)的實現(xiàn)铅歼,在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值换可,為了能夠讀取正確字節(jié)數(shù)椎椰,系統(tǒng)對象偏移量的指針類型進(jìn)行了類型強(qiáng)轉(zhuǎn)。
5. @protocol 和 category 中如何使用 @property沾鳄?
在 protocol 中使用 property 只會生成 setter 和 getter 方法聲明慨飘,我們使用屬性的目的,是希望遵守協(xié)議的對象能實現(xiàn)該屬性译荞。
category 使用 @property 也是只會生成 setter 和 getter 方法的聲明瓤的,如果我們真的需要給 category 增加屬性的實現(xiàn),需要借助于運行時的兩個函數(shù):
objc_setAssociatedObject
objc_getAssociatedObject
6. @property中有哪些屬性關(guān)鍵字吞歼?/ @property 后面可以有哪些修飾符堤瘤?
屬性可以擁有的特質(zhì)分為四類:
1.原子性--- nonatomic 特質(zhì)
在默認(rèn)情況下,由編譯器合成的方法會通過鎖定機(jī)制確保其原子性(atomicity)浆熔。如果屬性具備 nonatomic 特質(zhì),則不使用同步鎖桥帆。請注意医增,盡管沒有名為“atomic”的特質(zhì)(如果某屬性不具備 nonatomic 特質(zhì),那它就是“原子的” ( atomic) )老虫,但是仍然可以在屬性特質(zhì)中寫明這一點叶骨,編譯器不會報錯。若是自己定義存取方法祈匙,那么就應(yīng)該遵從與屬性特質(zhì)相符的原子性忽刽。
2.讀/寫權(quán)限---readwrite(讀寫)、readonly (只讀)
3.內(nèi)存管理語義---assign夺欲、strong跪帝、 weak、unsafe_unretained憔鬼、copy
4.方法名---getter=撼班、setter=
getter=的樣式:
@property (nonatomic, getter=isOn) BOOL on;
setter=一般用在特殊的情境下驯绎,比如:
在數(shù)據(jù)反序列化、轉(zhuǎn)模型的過程中黎泣,服務(wù)器返回的字段如果以 init 開頭恕刘,所以你需要定義一個 init 開頭的屬性,但默認(rèn)生成的 setter 與 getter 方法也會以 init 開頭抒倚,而編譯器會把所有以 init 開頭的方法當(dāng)成初始化方法褐着,而初始化方法只能返回 self 類型,因此編譯器會報錯托呕。
這時你就可以使用下面的方式來避免編譯器報錯:
@property(nonatomic, strong, getter=p_initBy, setter=setP_initBy:)NSString *initBy;
另外也可以用關(guān)鍵字進(jìn)行特殊說明含蓉,來避免編譯器報錯:
@property(nonatomic, readwrite, copy, null_resettable) NSString *initBy;
- (NSString *)initBy __attribute__((objc_method_family(none)));
不常用的:non null,null_resettable镣陕,nullable
7. weak屬性需要在dealloc中置nil么谴餐?
不需要。
在ARC環(huán)境無論是強(qiáng)指針還是弱指針都無需在 dealloc 設(shè)置為 nil 呆抑, ARC 會自動幫我們處理
我們模擬下 weak 的 setter 方法岂嗓,應(yīng)該如下:
- (void)setObject:(NSObject *)object{
objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{
_object = nil;
}];}
所以在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)鹊碍。
8. @synthesize和@dynamic分別有什么作用厌殉?
1.@property有兩個對應(yīng)的詞,一個是 @synthesize侈咕,一個是 @dynamic公罕。如果 @synthesize和 @dynamic都沒寫,那么默認(rèn)的就是@syntheszie var = _var;
@synthesize 的語義是如果你沒有手動實現(xiàn) setter 方法和 getter 方法耀销,那么編譯器會自動為你加上這兩個方法楼眷。
@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現(xiàn),不自動生成熊尉。(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)罐柳。假如一個屬性被聲明為 @dynamic var,然后你沒有提供 @setter方法和 @getter 方法狰住,編譯的時候沒問題张吉,但是當(dāng)程序運行到 instance.var = someVar,由于缺 setter 方法會導(dǎo)致程序崩潰催植;或者當(dāng)運行到 someVar = var 時肮蛹,由于缺 getter 方法同樣會導(dǎo)致崩潰。編譯時沒問題创南,運行時才執(zhí)行相應(yīng)的方法伦忠,這就是所謂的動態(tài)綁定。
9. ARC下稿辙,不顯式指定任何屬性關(guān)鍵字時缓苛,默認(rèn)的關(guān)鍵字都有哪些?
1.對應(yīng)基本數(shù)據(jù)類型默認(rèn)關(guān)鍵字是:
atomic , readwrite, assign
2.對于普通的 Objective-C 對象
atomic, readwrite, strong
10. 用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用copy關(guān)鍵字未桥,為什么笔刹?如果改用strong關(guān)鍵字,可能造成什么問題冬耿?
1.因為父類指針可以指向子類對象舌菜,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象亦镶,我本身持有的就是一個不可變的副本日月。
2.如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象缤骨,如果這個可變對象在外部被修改了爱咬,那么會影響該屬性。
copy 此特質(zhì)所表達(dá)的所屬關(guān)系與 strong 類似绊起。然而設(shè)置方法并不保留新值精拟,而是將其“拷貝” (copy)。 當(dāng)屬性類型為 NSString 時虱歪,經(jīng)常用此特質(zhì)來保護(hù)其封裝性蜂绎,因為傳遞給設(shè)置方法的新值有可能指向一個 NSMutableString 類的實例。這個類是 NSString 的子類笋鄙,表示一種可修改其值的字符串师枣,此時若是不拷貝字符串,那么設(shè)置完屬性之后萧落,字符串的值就可能會在對象不知情的情況下遭人更改践美。所以,這時就要拷貝一份“不可變” (immutable)的字符串找岖,確保對象中的字符串值不會無意間變動拨脉。只要實現(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
)
為了理解這種做法,首先要知道矛缨,兩種情況:
1.對非集合類對象的 copy 與 mutableCopy 操作爹脾;
2.對集合類對象的 copy 與 mutableCopy 操作。
1. 對非集合類對象的copy操作:
在非集合類對象中:對 immutable 對象進(jìn)行 copy 操作箕昭,是指針復(fù)制灵妨,mutableCopy 操作時內(nèi)容復(fù)制;對 mutable 對象進(jìn)行 copy 和 mutableCopy 都是內(nèi)容復(fù)制落竹。用代碼簡單表示如下:
1.[immutableObject copy] // 淺復(fù)制
2.[immutableObject mutableCopy] //深復(fù)制
3.[mutableObject copy] //深復(fù)制
4.[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ī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ù)制