對(duì)象、消息瓜晤、運(yùn)行時(shí)
理解“屬性”的概念
屬性的基礎(chǔ)用法就不多敘述了
屬性特質(zhì)
@property (nonatomic,readwrite,copy) NSString *name;
屬性擁有的特質(zhì)分為4類
1黍聂、原子性
atomic
設(shè)置成員變量的@property屬性時(shí),默認(rèn)為atomic瓮恭,提供多線程安全雄坪。
在多線程環(huán)境下,原子操作是必要的屯蹦,否則有可能引起錯(cuò)誤的結(jié)果维哈。加了atomic,setter函數(shù)會(huì)變成下面這樣:
{lock}
if (property != newValue) {
[property release];
property = [newValue retain];
}
{unlock}
nonatomic
禁止多線程颇玷,變量保護(hù)笨农,提高性能。 atomic是Objc使用的一種線程保護(hù)技術(shù)帖渠,基本上來講谒亦,是防止在寫未完成的時(shí)候被另外一個(gè)線程讀取,造成數(shù)據(jù)錯(cuò)誤空郊。而這種機(jī)制是耗費(fèi)系統(tǒng)資源的份招,所以在iPhone這種小型設(shè)備上,如果沒有使用多線程間的通訊編程狞甚,那么nonatomic是一個(gè)非常好的選擇锁摔。
指出訪問器不是原子操作,而默認(rèn)地哼审,訪問器是原子操作谐腰。這也就是說,在多線程環(huán)境下涩盾,解析的訪問器提供一個(gè)對(duì)屬性的安全訪問十气,從獲取器得到的返回值或者通過設(shè)置器設(shè)置的值可以一次完成,即便是別的線程也正在對(duì)其進(jìn)行訪問春霍。如果你不指定 nonatomic 砸西,在自己管理內(nèi)存的環(huán)境中,解析的訪問器保留并自動(dòng)釋放返回的值址儒,如果指定了 nonatomic 芹枷,那么訪問器只是簡單地返回這個(gè)值。
開發(fā)iOS程序時(shí)莲趣,應(yīng)該使用nonatomic屬性鸳慈,因?yàn)閍tomic屬性會(huì)嚴(yán)重影響性能。
2妖爷、讀/寫權(quán)限
readwrite會(huì)自動(dòng)生成getter/setter方法
readonly只有g(shù)etter方法
3蝶涩、內(nèi)存管理語義
assign “設(shè)置方法”只會(huì)執(zhí)行針對(duì)“純量類型”(CGFloat&&NSIntger等)的簡單賦值操作理朋。
strong 此特質(zhì)表明該屬性定義了一種“擁有關(guān)系”。賦值時(shí)會(huì)保留新值绿聘,并釋放舊值嗽上,然后再講新值設(shè)置上去
weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系”。賦值時(shí)既不保留新值熄攘,也不釋放舊值兽愤,同assign相似。然而屬性所指的對(duì)象銷毀時(shí)挪圾,屬性值也會(huì)被nil浅萧;
unsafe_unretained此特質(zhì)的語義和assign相同,但他適用于對(duì)象類型哲思,該特質(zhì)表達(dá)一種“非擁有關(guān)系”(“不保留”)洼畅,當(dāng)目標(biāo)對(duì)象遭到銷毀時(shí),屬性值不會(huì)被清空棚赔,與weak有區(qū)別帝簇。
copy所表達(dá)的所屬關(guān)系與strong相似。然而設(shè)置方法并不保留新值靠益,而是將其copy丧肴。當(dāng)屬性類型為NSString時(shí),經(jīng)常用此特質(zhì)來保護(hù)其封裝性胧后,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個(gè)NSMutableString類的實(shí)例芋浮。這個(gè)類是NSString的子類,表示可修改字符串壳快,此時(shí)若是不拷貝字符串纸巷,那么設(shè)置完屬性之后,字符串的值可能會(huì)在對(duì)象不知情的情況下遭到更改眶痰。
weak比 assign多了一個(gè)功能就是當(dāng)屬性所指向的對(duì)象消失的時(shí)候(也就是內(nèi)存引用計(jì)數(shù)為0)會(huì)自動(dòng)賦值為 nil何暇,這樣再向 weak修飾的屬性發(fā)送消息就不會(huì)導(dǎo)致野指針操作crash。
weak如何自動(dòng)置nil凛驮?
runtime 對(duì)注冊(cè)的類, 會(huì)進(jìn)行布局条辟,對(duì)于 weak 對(duì)象會(huì)放入一個(gè) hash 表中黔夭。 用 weak 指向的對(duì)象內(nèi)存地址作為 key,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì) dealloc羽嫡,假如 weak 指向的對(duì)象內(nèi)存地址是a本姥,那么就會(huì)以a為鍵, 在這個(gè) weak 表中搜索杭棵,找到所有以a為鍵的 weak 對(duì)象婚惫,從而設(shè)置為 nil氛赐。
用@property聲明的NSString(或NSArray,NSDictionary)經(jīng)常使用copy關(guān)鍵字先舷,為什么艰管?如果改用strong關(guān)鍵字,可能造成什么問題蒋川?
因?yàn)楦割愔羔樋梢灾赶蜃宇悓?duì)象,使用 copy 的目的是為了讓本對(duì)象的屬性不受外界影響,使用 copy 無論給我傳入是一個(gè)可變對(duì)象還是不可對(duì)象,我本身持有的就是一個(gè)不可變的副本.
如果我們使用是 strong ,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改了,那么會(huì)影響該屬性.
copy 此特質(zhì)所表達(dá)的所屬關(guān)系與 strong 類似牲芋。然而設(shè)置方法并不保留新值,而是將其“拷貝” (copy)捺球。 當(dāng)屬性類型為 NSString 時(shí)缸浦,經(jīng)常用此特質(zhì)來保護(hù)其封裝性,因?yàn)閭鬟f給設(shè)置方法的新值有可能指向一個(gè) NSMutableString 類的實(shí)例氮兵。這個(gè)類是 NSString 的子類裂逐,表示一種可修改其值的字符串,此時(shí)若是不拷貝字符串泣栈,那么設(shè)置完屬性之后卜高,字符串的值就可能會(huì)在對(duì)象不知情的情況下遭人更改。所以秩霍,這時(shí)就要拷貝一份“不可變” (immutable)的字符串篙悯,確保對(duì)象中的字符串值不會(huì)無意間變動(dòng)。只要實(shí)現(xiàn)屬性所用的對(duì)象是“可變的” (mutable)铃绒,就應(yīng)該在設(shè)置新屬性值時(shí)拷貝一份鸽照。
在對(duì)象內(nèi)部盡量直接訪問實(shí)例變量
1、訪問實(shí)例變量不經(jīng)過objc的方法派發(fā)(method dispatch)颠悬,所以速度更快矮燎。在這種情況下,編譯器所生成的代碼會(huì)直接訪問保存對(duì)象實(shí)例變量的那塊內(nèi)存赔癌。
2诞外、直接訪問實(shí)例變量,不會(huì)調(diào)用getter/setter方法
如果需要使用KVO或懶加載灾票,還是得使用屬性
在對(duì)象內(nèi)部讀取數(shù)據(jù)時(shí)峡谊,應(yīng)該直接通過實(shí)例變量來讀,而寫入數(shù)據(jù)時(shí)刊苍,應(yīng)該通過屬性來寫既们。
在初始化方法及dealloc方法中,總是應(yīng)該直接通過實(shí)例變量來讀寫數(shù)據(jù)正什。
有時(shí)會(huì)使用惰性初始化技術(shù)配置某份數(shù)據(jù)啥纸,這種情況下,需要通過屬性來讀取數(shù)據(jù)婴氮。
理解“對(duì)象等同性”
NSObject協(xié)議中有兩個(gè)用于判斷等同性的關(guān)鍵方法:
-(Bool)isEqual:(id)object;
-(NSUInteger)hash;
若想檢測(cè)對(duì)象的等同性斯棒,請(qǐng)?zhí)峁癷sEqual:”與hash方法盾致。
相同的對(duì)象必須具有相同的hash碼,但是兩個(gè)hash碼相同的對(duì)象卻未必相同荣暮。
不要盲目的逐個(gè)監(jiān)測(cè)每條屬性庭惜,而是應(yīng)該依照具體需求來制定檢測(cè)方案。
編寫hash方法時(shí)渠驼,應(yīng)該使用計(jì)算速度快而且哈希碼碰撞幾率低的算法蜈块。
理解objc_msgSend的作用
id returnValue = [someObject messageName:parameter];
someObject叫做receiver,messageName叫做selector迷扇,selector與參數(shù)合起來稱為message百揭。編譯器看到消息后,將其轉(zhuǎn)換為一條標(biāo)準(zhǔn)的C語言函數(shù)調(diào)用蜓席,如下
id returnValue = objc_msgSend(someObject,@selector(messageName:) parameter);
消息由receiver器一,selector及參數(shù)構(gòu)成。給某對(duì)象“發(fā)送消息”也就相當(dāng)于在該對(duì)象上調(diào)用方法厨内。
發(fā)給某對(duì)象的全部消息都要由“動(dòng)態(tài)消息派發(fā)系統(tǒng)”來處理祈秕,該系統(tǒng)會(huì)查出對(duì)應(yīng)的方法,并執(zhí)行其代碼雏胃。
理解消息轉(zhuǎn)發(fā)機(jī)制
什么是消息轉(zhuǎn)發(fā)请毛?當(dāng)對(duì)象接收到無法解讀的消息后,就會(huì)啟動(dòng)消息轉(zhuǎn)發(fā)機(jī)制瞭亮。
消息轉(zhuǎn)發(fā)分為兩個(gè)階段方仿。第一階段先征詢接收者所屬的類,看其是否能動(dòng)態(tài)添加方法统翩,已處理當(dāng)前這個(gè)“未知的selector”仙蚜,這叫做“動(dòng)態(tài)方法解析”。第二階段涉及“完整的消息轉(zhuǎn)發(fā)機(jī)制”厂汗。如果運(yùn)行期系統(tǒng)已經(jīng)把第一階段執(zhí)行完了委粉,那么接收者自己就無法再以動(dòng)態(tài)新增方法的手段來響應(yīng)包含該selector的消息了。此時(shí)運(yùn)行期系統(tǒng)就會(huì)請(qǐng)求接收者以其他手段來處理與消息相關(guān)的方法調(diào)用娶桦。細(xì)分為兩步:首先贾节,讓接收者看看有沒有其他對(duì)象能處理這條消息。如果有衷畦,則運(yùn)行期系統(tǒng)會(huì)把消息轉(zhuǎn)給那個(gè)接收者氮双,于是消息轉(zhuǎn)發(fā)結(jié)束。如果沒有這個(gè)“備援接收者”霎匈,則啟動(dòng)完整的消息轉(zhuǎn)發(fā)機(jī)制,運(yùn)行期系統(tǒng)會(huì)把與消息有關(guān)的全部細(xì)節(jié)封裝到NSInvocation對(duì)象中送爸,再給接收者最后一次機(jī)會(huì)铛嘱,令其設(shè)法解決當(dāng)前還未處理的這條消息暖释。
若對(duì)象無法響應(yīng)某個(gè)selector,則進(jìn)入消息轉(zhuǎn)發(fā)流程墨吓。
通過運(yùn)行期的動(dòng)態(tài)方法解析功能球匕,我們可以在需要用到某個(gè)方法時(shí)再將其加入類中。
對(duì)象可以將其無法解讀的某些selector轉(zhuǎn)交給其他對(duì)象處理帖烘。
經(jīng)過上述兩步后亮曹,如果還是沒辦法處理selector,那就啟動(dòng)完整的消息轉(zhuǎn)發(fā)機(jī)制秘症。
用method swizzling調(diào)試黑盒方法
在runtime中照卦,可以向類中新增或替換selector所對(duì)應(yīng)的方法實(shí)現(xiàn)。
使用另一份實(shí)現(xiàn)來替換原有的方法實(shí)現(xiàn)乡摹,這道工序叫做method swizzling役耕,開發(fā)者常用此技術(shù)向原有視線中添加功能。
一般來說聪廉,只有調(diào)試程序的時(shí)候才需要在runtime中修改方法實(shí)現(xiàn)瞬痘,這種做法不宜濫用。