協(xié)議和范疇類
協(xié)議可以用來聲明能夠在任何類中實(shí)現(xiàn)的方法,甚至那些實(shí)現(xiàn)該方法的類繼承自別的類。協(xié)議方法定義的行為是獨(dú)立于任何一個(gè)類的。協(xié)議可以定義一個(gè)要求其他類必須承諾實(shí)現(xiàn)的接口。也就是說,如果你的類實(shí)現(xiàn)了協(xié)議中的方法,那么這個(gè)類就承諾要完成該協(xié)議。
從實(shí)用的角度說來,協(xié)議定義了一系列方法,并建立起對(duì)象之間的“合約”。而這些對(duì)象不必是任何一個(gè)確定的類的實(shí)例。這個(gè)合約使得對(duì)象之間可以進(jìn)行交流。某個(gè)對(duì)象想要告訴另一個(gè)對(duì)象,馬上將要面臨的事件,或者想要詢問關(guān)于那些事件的建議。
UIApplication 類實(shí)現(xiàn)了所需的應(yīng)用行為。你不必為了接收簡(jiǎn)單的應(yīng)用當(dāng)前狀態(tài)的消息而創(chuàng)建一個(gè) UIApplication 的子類。UIApplication 類會(huì)調(diào)用指定的委托對(duì)象中的特定方法來傳遞那些消息。實(shí)現(xiàn)了 UIApplicationDelegate 協(xié)議方法的對(duì)象就能夠接收到那些消息了,并且能夠提供合適的反饋。
在承諾實(shí)現(xiàn)、或采用某個(gè)協(xié)議的接口代碼中,協(xié)議的名稱要寫在父類名稱后邊的一對(duì)尖括號(hào)里(<…>)。在“你的第一個(gè) iOS 應(yīng)用”教程里,你采用了 UITextFieldDelegate 協(xié)議:
@interface HelloWorldViewController : UIViewController {
}
@end
在實(shí)現(xiàn)中,你無需聲明協(xié)議方法。
協(xié)議的聲明看起來和類接口很相似,不過不同的是協(xié)議沒有父類,并且不含任何實(shí)例變量(但它們能夠聲明屬性)。下面的例子展示如何聲明只有一個(gè)方法的簡(jiǎn)單協(xié)議:
@protocol MyProtocol
- (void)myProtocolMethod;
@end
對(duì)于許多委托協(xié)議而言,采用一個(gè)協(xié)議就等于是實(shí)現(xiàn)該協(xié)議中定義的方法。有些協(xié)議要求你明確聲明你會(huì)支持該協(xié)議,而有些協(xié)議則是既包含必須實(shí)現(xiàn)的方法,也包含可選方法。
當(dāng)你在 Objective-C 框架中瀏覽頭文件時(shí),你很快就會(huì)看到類似這樣的語句:
@interface NSDate (NSDateCreation)
這行語句聲明了一個(gè)范疇類(category),其語法是將范疇類的名稱包裹在一對(duì)圓括號(hào)中。范疇類是 Objective-C 語言的一個(gè)特性,讓你能夠擴(kuò)展某個(gè)類的接口,而無需創(chuàng)建它的子類。范疇類中的方法將成為此類的一部分(在你的程序作用域范圍內(nèi)),并會(huì)被此類的所有子類繼承。你可以向此類(或它的子類)的任意一個(gè)實(shí)例發(fā)送消息來調(diào)用范疇類中聲明的方法。
你可以利用范疇類在一個(gè)頭文件里組織互相關(guān)聯(lián)的方法聲明。你甚至可以在不同的頭文件中放入不同的范疇類聲明。Cocoa Touch 框架和 Cocoa 框架在幾乎所有頭文件中都利用了這個(gè)技術(shù),代碼才如此明晰。你還能使用匿名范疇類(也就是在圓括號(hào)中不寫任何字符),這樣可以把實(shí)例變量隱藏在私有的實(shí)現(xiàn)文件里。
預(yù)定義類型和編碼策略
在 Objective-C 語言中有一些特定的詞匯,你要避免在聲明變量時(shí)使用這些詞匯,因?yàn)樗鼈兌加袑iT的用途。其中有一些是編譯器指令,以 @ 符號(hào)開頭(例如 @interface 和 @end)。有些保留詞匯是預(yù)定義類型,以及和這些類型有關(guān)的文字。Objective-C 使用一系列不屬于 ANSI C 的預(yù)定義類型和詞匯。在某些情況下,這些類型和詞匯會(huì)代替它們?cè)?ANSI C 中的對(duì)應(yīng)者。下表列出了幾個(gè)非常重要的類型,包括每個(gè)類型所允許的詞匯。
類型、描述和詞匯:
id – 動(dòng)態(tài)對(duì)象類型。動(dòng)態(tài)類型和靜態(tài)類型對(duì)象的否定詞匯為 nil。
Class – 動(dòng)態(tài)類的類型。它的否定詞匯為 Nil。
SEL – 選擇器的數(shù)據(jù)類型(typedef);這種數(shù)據(jù)類型代表運(yùn)行時(shí)的一種簽名方法。它的否定詞匯為 NULL。
BOOL – 布爾型。代表它的值的詞匯為 YES 和 NO。
你通常會(huì)在代碼排錯(cuò)以及流程控制中使用這些預(yù)定義的類型和詞匯。在程序的流程控制語句中,你可以通過檢測(cè)特定詞匯來判斷如何采取下一步動(dòng)作。例如:
NSDate *dateOfHire = [employee dateOfHire];
if (dateOfHire != nil) {
// 處理此種情況
}
把代碼解釋一下:如果對(duì)象代表的聘用日期不為 nil,也就是說是一個(gè)合法的對(duì)象,那么邏輯就朝一個(gè)特定的方向發(fā)展。下邊是這段代碼的簡(jiǎn)化版,效果是相同的:
NSDate *dateOfHire = [employee dateOfHire];
if (dateOfHire) {
// 處理此種情況
}
你甚至可以把代碼簡(jiǎn)化成這個(gè)樣子(前提是你不需要引用 dateOfHire 對(duì)象):
if ([employee dateOfHire]) {
// 處理此種情況
}
處理布爾值的方法是一樣的。在這個(gè)例子中,isEqual: 方法會(huì)返回一個(gè)布爾型的值:
BOOL equal = [objectA isEqual:objectB];
if (equal == YES) {
// 處理此種情況
}
你同樣可以按照前邊省略 nil 的方式來簡(jiǎn)化這段代碼。
在 Objective-C 語言里,你可以對(duì) nil 發(fā)送消息,不必?fù)?dān)心任何副作用。其實(shí),是根本不會(huì)有任何作用,只不過運(yùn)行時(shí)會(huì)返回 nil,如果方法本來要返回一個(gè)對(duì)象的話。發(fā)送給 nil 的消息返回的值只要是一個(gè)對(duì)象,代碼就能繼續(xù)正常工作。
另外兩個(gè)重要的預(yù)留詞匯是 self 和 super。前者 self 是個(gè)局部變量,你可以在消息實(shí)現(xiàn)中把它看做當(dāng)前對(duì)象進(jìn)行引用;它和 C++ 語言中的 this 是一樣的。你可以用預(yù)留詞匯 super 替換 self,但只能作為消息表達(dá)式的接收者。如果你對(duì) self 發(fā)送了消息,那么運(yùn)行時(shí)首先就會(huì)在這個(gè)對(duì)象的類中尋找相應(yīng)的方法實(shí)現(xiàn);如果這里找不到,那么它會(huì)轉(zhuǎn)而到其父類中去查找(如此往復(fù))。如果你對(duì) super 發(fā)送消息,運(yùn)行時(shí)首先就是去父類中尋找方法的實(shí)現(xiàn)。
利用 self 和 super 主要是為了發(fā)送消息。當(dāng)被調(diào)用的方法在 self 的類中被實(shí)現(xiàn)之后,你就可以向 self 發(fā)送消息,例如:
[self doSomeWork];
self 還可以用在點(diǎn)語法中,用來調(diào)用由已聲明的屬性生成的存取方法,例如:
NSString *theName = self.name;
你常常會(huì)在重載(既重新實(shí)現(xiàn)已有的方法)從父類繼承而來的方法時(shí)向 super 發(fā)送消息。在這種情況下,被調(diào)用的方法擁有和重載后方法相同的簽名。
|