應(yīng)用和運(yùn)行回路
運(yùn)行回路
應(yīng)用從操作系統(tǒng)中接收鼠標(biāo)點(diǎn)擊等事件的消息嘹屯,并將其轉(zhuǎn)到相應(yīng)的例行程序來處理帖鸦,如此反復(fù)抑诸,這樣的過程被稱為運(yùn)行回路(run loop)或事件循環(huán)(event loop)谍椅。
因?yàn)镃ocoa應(yīng)用本身就有GUI功能趴荸,所以一旦開始運(yùn)行轿秧,就一定會(huì)產(chǎn)生一個(gè)運(yùn)行回路中跌,它也稱為主運(yùn)行回路。同時(shí)菇篡,應(yīng)用的事件處理或資源的管理功能需要有一個(gè)對(duì)象來完成漩符,所以Mac OS中提供了NSApplication類的實(shí)例,iOS中提供了UIApplication類的實(shí)例驱还,該實(shí)例會(huì)根據(jù)操作系統(tǒng)發(fā)送的事件選擇對(duì)應(yīng)的處理對(duì)象嗜暴,并發(fā)送相應(yīng)的消息。
回路運(yùn)行后议蟆,當(dāng)有新的事件消息到來而別的處理還沒有結(jié)束的情況下闷沥,應(yīng)用就可以把該事件放入等待隊(duì)列中并在之后按順序執(zhí)行。這種性質(zhì)非常適合多線程的異步執(zhí)行咐容。
在使用引用計(jì)數(shù)的方式的情況下舆逃,主運(yùn)行回路在啟動(dòng)事件處理方法時(shí)會(huì)生成一個(gè)自動(dòng)釋放池,并在方法終止的時(shí)候釋放它戳粒。而垃圾回收的情況下路狮,一個(gè)事件處理完后就會(huì)啟動(dòng)一個(gè)垃圾回收器。應(yīng)用中執(zhí)行的方法基本上是自己管理自動(dòng)釋放池蔚约,不需要擔(dān)心什么時(shí)候進(jìn)行垃圾回收奄妨。而主線程之外執(zhí)行的方法則要自己生成自動(dòng)釋放池。
定時(shí)器對(duì)象
現(xiàn)在介紹一下能夠在特定的時(shí)間或者按照一定的時(shí)間間隔來發(fā)送指定消息的組件苹祟。靈活使用該組件砸抛,就能夠輕松實(shí)現(xiàn)一些必須并行處理才能夠?qū)崿F(xiàn)的操作。
NSTimer類能夠讓指定的消息在一定的時(shí)間間隔后執(zhí)行树枫。而定時(shí)器對(duì)象即實(shí)現(xiàn)了NSTimer類的實(shí)例直焙。
定時(shí)器的使用必須要有運(yùn)行回路。在運(yùn)行回路上注冊(cè)定時(shí)器后砂轻,到達(dá)規(guī)定的時(shí)間時(shí)箕般,運(yùn)行回路就會(huì)調(diào)用注冊(cè)的方法來處理。
更多有關(guān)NSTimer的相關(guān)介紹請(qǐng)參考相關(guān)文檔舔清。
消息的延遲執(zhí)行
下面介紹一下經(jīng)過一段時(shí)間后在執(zhí)行消息的方法丝里。該方法由NSObject定義曲初,所有對(duì)象都可以使用。同樣延遲執(zhí)行也要有運(yùn)行回路杯聚。
- (void)performSelector:(SEL)aSelector withObject:(nullableid)anArgument afterDelay:(NSTimeInterval)delay
//該消息被發(fā)送后臼婆,至少要經(jīng)過delay秒,aSelector指定的消息才會(huì)真正發(fā)送給anArgument幌绍。
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector withObject:(id)anArgument
//取消先前執(zhí)行的請(qǐng)求颁褂。
委托
委托的概念
當(dāng)對(duì)象需要根據(jù)用途改變或增加新功能時(shí),為了執(zhí)行新添加的處理傀广,就需要引用一個(gè)特殊的類似于“被咨詢者”的對(duì)象颁独。這個(gè)對(duì)象就稱為委托(delegate)。委托可在運(yùn)行時(shí)動(dòng)態(tài)分配伪冰。
委托是對(duì)象之間分擔(dān)功能并協(xié)同處理時(shí)的一個(gè)典型的設(shè)計(jì)模式誓酒。在面向?qū)ο笾校幸话憧梢员唤忉尀椤澳硨?duì)象接收到不能處理的消息時(shí)讓其他對(duì)象帶為處理的一種方式”贮聂。
Cocoa環(huán)境中的委托
在Cocoa環(huán)境中靠柑,委托是應(yīng)用為了添加必要的處理而“增設(shè)”的對(duì)象。
委托具有很高的可復(fù)用性吓懈,在作為組件使用的類中十分有效歼冰。通過使用委托,可以在不損害原類別的獨(dú)立性的同時(shí)耻警,給軟件增加獨(dú)立的功能隔嫡。
一個(gè)對(duì)象并不是只能有一個(gè)委托,某些情況下一個(gè)對(duì)象也可以擁有多個(gè)委托甘穿。
在某種程度上腮恩,使用繼承也可以達(dá)到相同的效果,但是繼承在運(yùn)行時(shí)無法靈活的分配扒磁。繼承和委托必須要區(qū)分使用庆揪,但委托有很多繼承沒有的優(yōu)點(diǎn)式曲。
委托的設(shè)置和協(xié)議
設(shè)置委托妨托,返回委托對(duì)象的方法使用如下函數(shù):
- (void)setDelegate:(id)anObject
//將參數(shù)對(duì)象設(shè)置為委托。一般不需要保留(retain)參數(shù)對(duì)象吝羞。是顯式調(diào)用委托的協(xié)議兰伤。
- (id)delegate
//返回接受的委托對(duì)象。
在實(shí)際編程時(shí)钧排,除了要關(guān)注各類中可以調(diào)用的方法外敦腔,還需要注意可以用委托實(shí)現(xiàn)什么樣的功能。
如果要將某個(gè)對(duì)象作為委托使用恨溜,就需要在該類的接口部分中聲明使用委托的協(xié)議符衔。定義實(shí)現(xiàn)委托方法的范疇就是一個(gè)好方法找前。而且,一個(gè)對(duì)象可以作為多個(gè)類的委托判族。此時(shí)將使用與之相對(duì)應(yīng)的多個(gè)協(xié)議躺盛。
聲明委托的協(xié)議時(shí),基本上所有的方法中都要指定@optional選項(xiàng)形帮。持有委托的對(duì)象會(huì)在運(yùn)行時(shí)檢查該委托所能處理的消息槽惫。委托中沒實(shí)現(xiàn)的消息不會(huì)被發(fā)送。所以委托對(duì)象只需實(shí)現(xiàn)相應(yīng)的處理方法即可辩撑。
此外界斜,由于某些情況下向委托發(fā)送消息是以存在運(yùn)行回路為前提的,因此我們就需要注意生成命令行程序時(shí)的情況合冀。
自定義類雖然也可以有委托的功能各薇,但是實(shí)現(xiàn)起來并不簡(jiǎn)單。實(shí)現(xiàn)時(shí)需要使用respondsToSelector:等來檢查是否可以處理某個(gè)消息水慨。
通知
通知和通知中心的概念
在面向?qū)ο蟮某绦蛑械妹樱袝r(shí)也需要將發(fā)生的事件通知給多個(gè)對(duì)象。
在Foundation框架中晰洒,為了執(zhí)行某一行為朝抖,程序需要將這一行為向多個(gè)對(duì)象發(fā)出通知,這種消息發(fā)送方式就稱為通知(notification)谍珊。
程序內(nèi)提供了通知中心這個(gè)對(duì)象治宣。(notification center)這個(gè)對(duì)象。期望取得通知的對(duì)象預(yù)先向通知中心注冊(cè)期望取得的通知砌滞。通知有多種類型侮邀,分別以通知名來標(biāo)記,也可以自定義新的通知名贝润。
某對(duì)象向通知中心發(fā)送消息發(fā)送請(qǐng)求绊茧,這稱為發(fā)送(post)通知。只要是注冊(cè)過該通知的對(duì)象打掘,都會(huì)獲得通知中心推送的消息华畏。
消息發(fā)送目標(biāo),也就是在通知中心注冊(cè)的對(duì)象尊蚁,稱為觀察者(observer)亡笑。對(duì)象在通知中心將自己注冊(cè)為觀察者時(shí),會(huì)指定接受什么名字的通知以及什么什么選擇器的消息横朋。而且仑乌,也可以指定只接收特定對(duì)象發(fā)送(post)的通知。觀察者很多或者完全沒有都沒有關(guān)系。
無論什么對(duì)象都可以自由的使用通知的發(fā)送機(jī)制晰甚,而且不需要在通知中心注冊(cè)衙传。兩者之間必須共享的僅僅是通知的名字。
這樣的機(jī)制既不會(huì)降低發(fā)送通知的對(duì)象和接收通知的觀察者之間的獨(dú)立性厕九,又可以實(shí)現(xiàn)向多個(gè)對(duì)象發(fā)送消息粪牲。
某個(gè)對(duì)象向特定的多個(gè)對(duì)象發(fā)送消息的通信形式稱為多播(multicast),向非特定的多個(gè)對(duì)象發(fā)送消息稱為廣播(brodcast)止剖。
通知對(duì)象
向通知中心發(fā)送消息時(shí)腺阳,必要的信息會(huì)在NSNotification類實(shí)例中集中后發(fā)送給通知中心。這個(gè)對(duì)象就被稱為通知對(duì)象穿香,或者簡(jiǎn)稱為通知亭引。觀察者從通知中心取得消息時(shí)也會(huì)取得附帶的通知對(duì)象。
通知對(duì)象包含以下信息:
名字:
用來識(shí)別通知的短文本皮获。用如下消息取出焙蚓。
- (NSString *)name
對(duì)象:
和通知一起發(fā)送的附帶信息的對(duì)象。多為發(fā)送通知的對(duì)象洒宝,也可以為nil购公。用如下消息取出。
- (id)object
用戶字典(userInfo)
為了傳遞和通知相關(guān)的各種信息而使用NSDictionary字典雁歌。用如下消息取出宏浩。
- (NSDictionary *)userInfo
因?yàn)橄蛲ㄖ行陌l(fā)送消息時(shí)會(huì)自動(dòng)創(chuàng)建通知對(duì)象,所以沒有必要自己創(chuàng)建靠瞎。創(chuàng)建通知對(duì)象比庄,可以使用下面任意一種NSNotification的類方法。
+ (id)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)userInfo
//指定通知名乏盐,對(duì)象和用戶字典佳窑,生成臨時(shí)通知對(duì)象。
+ (id)notificationWithName:(NSString *)aName obect:(id)anObject
//指定通知名和對(duì)象生成臨時(shí)通知對(duì)象父能。用戶信息字典為nil神凑。
通知中心
通知中心使用NSNotificationCenter的類接口實(shí)現(xiàn)。
1.默認(rèn)的通知中心
各進(jìn)程都會(huì)預(yù)先準(zhǔn)備一個(gè)通知中心何吝,一般不需要自己創(chuàng)建溉委。這就是默認(rèn)的通知中心,可以使用如下的類方法獲得岔霸。
+ (id)defaultCenter
2.通知的發(fā)送
相關(guān)方法參考文檔
3.觀察者注冊(cè)
相關(guān)方法參考文檔
4.刪除觀察者注冊(cè)
相關(guān)方法參考文檔
這里需要格外注意的內(nèi)存管理薛躬。首先俯渤,使用引用計(jì)數(shù)管理的情況下呆细,通知中心在注冊(cè)觀察者時(shí),并不保留(retain)觀察者及發(fā)送源對(duì)象。所以絮爷,在釋放這些對(duì)象之前趴酣,要確實(shí)從通知中心刪除相關(guān)設(shè)置。如果不這樣的話坑夯,指向釋放對(duì)象的指針將成為空指針岖寞。
使用垃圾回收機(jī)制時(shí),觀察者和發(fā)送源對(duì)象使用弱引用在通知中心注冊(cè)柜蜈。無需顯式刪除觀察者的注冊(cè)仗谆。
通知隊(duì)列
通知隊(duì)列是將通知對(duì)象臨時(shí)存儲(chǔ)在等待隊(duì)列中,然后按照FIFO的原則向通知中心發(fā)送消息淑履。通知隊(duì)列是通過NSNotificationQueue實(shí)現(xiàn)的隶垮。詳細(xì)介紹參考相關(guān)文檔。
1.異步發(fā)送
將通知追加到隊(duì)列秘噪,然后在運(yùn)行回路完成當(dāng)前處理或者運(yùn)行回路中的輸入都被處理完成后狸吞,再發(fā)送通知。通過使用該方法指煎,就可以直接終止隊(duì)列中追加的方法并進(jìn)行下一項(xiàng)處理蹋偏。這樣一來,通知的發(fā)送就會(huì)在一連串處理之后再執(zhí)行至壤。這種方式也稱為異步(asychronous)發(fā)送威始。一般的處理方式是同步(synchronous)發(fā)送。
2.合并相同的通知
通知隊(duì)列中有相同的通知時(shí)像街,將它們合并為一個(gè)字逗,以此來刪除多余的通知。
通知名或異常名的定義:
自定義使用通知名或異常名等時(shí)宅广,一般都不直接使用常數(shù)字符串葫掉,而是用全局變量或宏名來定義。
反應(yīng)鏈
反應(yīng)鏈概述
反應(yīng)鏈(responder chain)是具有層次結(jié)構(gòu)的GUI組件間自動(dòng)發(fā)送消息的一種方式跟狱。
處理消息的候補(bǔ)組件對(duì)象如算珠般串聯(lián)在一起俭厚,將消息傳遞給這之間的某個(gè)對(duì)象時(shí),在發(fā)現(xiàn)可以處理該消息的對(duì)象之前驶臊,會(huì)順次向后后面的對(duì)象轉(zhuǎn)交該消息挪挤。這樣的構(gòu)成就稱為反應(yīng)鏈。
在Mac OS 中关翎,按鍵扛门,滑塊,文本域等組件以及窗體纵寝,面板等對(duì)象都是Application框架的NSResponder類的子類论寨。同樣,在iOS中是UIKit框架的UIResponder類的子類。
各窗體中葬凳,最初被發(fā)送消息的對(duì)象稱為第一反應(yīng)者(first responder)绰垂。第一反應(yīng)者都是光標(biāo)最近選擇的對(duì)象。
應(yīng)用中的反應(yīng)鏈
在Mac OS 平臺(tái)下火焰,在應(yīng)用窗口中顯示在最前端的窗口稱為主窗口劲装。在包含面板等在內(nèi)的其他窗口中,最前面的窗口稱為關(guān)鍵窗口昌简。主窗口也可以是關(guān)鍵窗口占业。
反應(yīng)鏈內(nèi)的搜索,首先從關(guān)鍵窗口的第一反應(yīng)者開始纯赎。關(guān)鍵窗口中無法應(yīng)對(duì)找不到時(shí)纺酸,如果存在其他主窗口,主窗口的反應(yīng)鏈也會(huì)按同樣的方式搜索址否。
有時(shí)主窗口也不能處理時(shí)餐蔬,消息最終會(huì)被傳給NSApplication(管理應(yīng)用的全體的類)的接口。這樣一來佑附,不能處理的消息就會(huì)被忽視樊诺,或者響起警告音。
在iOS平臺(tái)下音同,表示繪圖區(qū)域的類UIView是其他GUI組件的超類词爬,同時(shí)也是表示反應(yīng)者的UIResponder的子類。接口由窗口(UIWindow類)权均,UIView類及其子類GUI組件有層次地重疊構(gòu)成顿膨。UIKit會(huì)自動(dòng)將任意一個(gè)按鍵或文本域作為第一反應(yīng)者。第一反應(yīng)者不能處理的消息叽赊,將沿著重疊的GUI組件恋沃,被有序地向窗口甚至引用程序(UIApplication類)傳遞。
在Cocoa環(huán)境中必指,生成包含GUI的應(yīng)用必須掌握反應(yīng)鏈相關(guān)的知識(shí)囊咏。詳細(xì)內(nèi)容參考“Cocoa Event-Handing Guide” “Event-Handing Guide for iOS"
消息轉(zhuǎn)送
消息轉(zhuǎn)送的構(gòu)成
將消息發(fā)送給沒有實(shí)現(xiàn)該消息方法的對(duì)象時(shí),通常會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)誤塔橡。但是梅割,我們可以將不能被處理的消息轉(zhuǎn)送給其他對(duì)象,這樣的功能是可以實(shí)現(xiàn)的葛家。
將某消息發(fā)送到相應(yīng)的接收者户辞。如果接收者沒有實(shí)現(xiàn)應(yīng)對(duì)消息的方法,運(yùn)行時(shí)系統(tǒng)就會(huì)發(fā)送如下消息給接收者癞谒。
- (void)forwardInvocation:(NSInvocation *)anInvocation
//這個(gè)方法在NSObject中定義底燎,所有對(duì)象都可以處理刃榨。在NSObject中,這個(gè)方法可以調(diào)用如下方法
- (void)doesNotRecognizeSelector:(SEL)aSelector
//生成錯(cuò)誤消息书蚪,表示異常NSInvalidArgumentException發(fā)生,無法處理參數(shù)選擇器對(duì)應(yīng)的消息迅栅。
也就是說殊校,只要接收者重定義了forwardInvocation:方法,當(dāng)被發(fā)送不能處理的消息時(shí)读存,就可以將其轉(zhuǎn)送給其他對(duì)象为流,或者自己執(zhí)行錯(cuò)誤處理。
消息轉(zhuǎn)送需要的信息
NSInvocation的對(duì)象中保存了目標(biāo)让簿,選擇器敬察,參數(shù)等消息發(fā)送所必須的全部元素。
下面是NSInvocation類的主要方法尔当。
- (SEL)selector
//返回設(shè)定的選擇器
- (id)target
//返回設(shè)定的目標(biāo)
- (void)invokeWithTarget:(id)anObject
//向目標(biāo)參數(shù)對(duì)象發(fā)送表示接收者的消息莲祸。將消息的結(jié)果返回給源發(fā)送者。
消息轉(zhuǎn)送的定義
使用上述的NSInvocation信息椭迎,forwardInvocation:就可以被重新定義锐帜,從而實(shí)現(xiàn)消息轉(zhuǎn)送。但是畜号,可以轉(zhuǎn)送的僅僅是參數(shù)個(gè)數(shù)固定的方法缴阎。
例如,調(diào)查不能處理的消息是否可以轉(zhuǎn)送給對(duì)象fellow简软,當(dāng)不能轉(zhuǎn)送則交由超類處理蛮拔,可采用如下方式定義:
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SELsel = [anInvocation selector];
if([fellow respondToSelector:sel])
[anInvocation invokeWithTarget:fellow];
else
[superforwardInvocation];
}
再有為了使運(yùn)行時(shí)系統(tǒng)能夠使用轉(zhuǎn)送目的地的對(duì)象信息生成NSInvocation實(shí)例,必須重新定義返回的方法簽名(method signature)對(duì)象痹升。
為了表示方法簽名建炫,定義了NSMethodSignature類。它的對(duì)象保存著方法的參數(shù)和返回值疼蛾,但在消息轉(zhuǎn)送或通信之外的情況下踱卵,程序是不處理的。
返回方法簽名的方法在methodSignatureForSelector:和NSObject中定義据过。例如轉(zhuǎn)送目的地是fellow惋砂,就可以按如下方式重新定義。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if([superrespondsToSelector:aSelector])
return[supermethodSignatureForSelector:aSelector];
return[fellow methodSignatureForSelector:aSelector];
}
然而绳锅,使用轉(zhuǎn)送方法處理的消息不能被respondsToSelector:等調(diào)用西饵。當(dāng)需要知道該消息是否為目標(biāo)對(duì)象可處理的消息時(shí),必須重定義respondsToSelector:等方法鳞芙。
禁止使用消息
如前所述眷柔,方法doesNotRecognizedSelector:表示消息不能處理時(shí)所產(chǎn)生的異常期虾。
利用此特性,在使用某個(gè)方法時(shí)就可以定義運(yùn)行時(shí)產(chǎn)生的錯(cuò)誤驯嘱。一個(gè)典型的例子就是镶苞,當(dāng)用超類定義的方法在子類不能使用時(shí),可以寫下該方法及時(shí)禁止其使用鞠评。
例如茂蚓,想禁止使用方法setSize:時(shí),可采用如下方式定義剃幌。這里_cmd為方法的隱藏參數(shù)聋涨,表示方法的選擇器。所以寫下@selector(setSize:)也是一樣的负乡。
- (void)setSize:(NSSize *)size {
[selfdoesNotRecognizeSelector:_cmd];
}