2011-01-29 28 views
4

如果我不得不在對象上調用「respondsToSelector:」,那麼將方法定義爲可選對象對我來說又有什麼用處呢?協議中可選方法的要點是什麼?

對於exmple,讓我們說我有這樣

id<MyProtocol> myObj = [[MyClass alloc] init]; 
if([myObj respondsToSelector:@selector(aMethod)]){ 
    [myObject aMethod]; 
} 

一些代碼,只要「MyClass的」實現「amethod方法」,會不會這個代碼的運行完全相同的羯羊或不MyProtocol定義「amethod方法」 ?

我可以看到使用純粹從代碼可讀性的角度來定義這個可選協議,但不明白它是否從技術角度來看實際上有任何影響(除了不必在頭中聲明方法) 。

回答

8

它幾乎和罐頭上說的一樣:它與可選功能相關。如果您的aMethod包含必不可少的功能爲了您的應用程序的工作應該是@required。否則,如果它爲實施者提供其他功能以執行其他操作,但缺席不會對其工作方式產生負面影響(即實施者沒有對@selector(aMethod)選擇器做出響應),則可以將其設置爲@optional

您看到這很多在視圖委託協議。採取的iOS的UITableViewDelegate例如:這裏是一組的代表協議的方法定義視圖爲表視圖的部分的頁眉和頁腳:

  • tableView:viewForHeaderInSection:
  • tableView:viewForFooterInSection:
  • tableView:heightForHeaderInSection:
  • tableView:heightForFooterInSection:

如果這些不是由委託實現的,UIKit只是繪製默認的節頭和頁腳給定的tableView,它與預設的UITableView元素預包裝在一起。但是,如果它們被實現,則UIKit使用由tableView這些方法提供的自定義區域頁眉和頁腳視圖。

@optional關鍵字幾乎告訴寫一個類來實現委託,這些方法是可選的。無論如何,我相信UIKit會在內部檢查conformsToProtocol:respondsToSelector:

+0

感謝BoltClock。我瞭解可能實施該方法的價值,也許不是。但我不明白在協議中如何定義它,如果我需要調用respondsToSelector。 – morgancodes

+0

@morgancodes:它似乎純粹是爲了語義。 – BoltClock

+0

那麼,對於人們而言,比對於機器而言更多? – morgancodes

1

@optional/@required不只是爲人們 - 儘管這本身就是一個很好的理由! - 它也適用於機器。編譯器,靜態分析器等可以使用它們來確定實現@protocol的類是否提供了所有必需的方法,並確定提供了哪些可選方法。

-1

Morgancodes,

您不必調用respondsToSelector在協議中聲明,如果您使用的協議進行類型檢查,以及每一個方法。一個簡單的解釋可以發現Here

弗蘭克

+0

您仍然需要爲可選定義執行此操作。 –

0

還看到蘋果公司的Communicating with Objects,其中討論的代表,協議和選擇。雖然它在Mac OS X中列出,但大多數(如果不是全部的話)似乎也適用於iOS。

0

還有一點需要指出 - 在發送帶有特定名稱的消息之前,必須聲明具有該名稱的方法某處對於其使用的範圍可見。 (它不必被聲明爲你發送消息的特定類型的方法 - 這是一個單獨的問題,動態類型和靜態類型檢查,但它必須被聲明爲作爲的方法一些類或協議,即使根本不使用該類或協議)。這是由於編譯器必須將消息調用轉換爲objc_msgSendobjc_msgSend_stretobjc_msgSend_fpret,這取決於方法的返回類型;所以編譯器必須知道方法的簽名。

所以一個可選的協議方法用於聲明該方法的目的;而如果您沒有在協議中包含該方法聲明,由於沒有聲明,調用方可能無法調用該方法。

0

協議僅用於文檔和編譯時檢查。程序運行後,運行時並不知道或關心對象的靜態類型(除了newacct提到的方法返回結構的情況外)。

除了沒有編譯時檢查某個特定對象在傳遞給特定API時是否具有所需的方法時,您可以不使用協議。如果你喜歡,你可以聲明所有的對象類型爲id,但是這樣可以有效地關閉編譯器,檢查你發送給它們的消息是否由對象實現。它也阻止你使用點符號來表示屬性。

鑑於您有協議,一旦您聲明一個對象符合協議,例如

id<MyProtocol> foo; 

立即將方法檢查重新打開。沒有可選方法,這意味着

if ([foo respondsToSelector: @selector(myOptionalSelector)]) 
{ 
    [foo myOptionalSelector]; 
} 

會標記編譯器警告。放置@optional方法可以抑制該警告,並停止編譯器不必猜測返回值和參數類型。

+0

這不完全正確;協議也可以在運行時進行交互。請參閱Objective-C運行時參考和方法,例如+ [NSObject conformsToProtocol:]。 – AriX

0

我認爲最大的區別就是調用方法!

如果您未定義協議,則需要使用方法performSelector:調用未知方法。它有效,但在這種情況下,我們受限於您的方法的參數數量和類型。事實上,你不能給出非對象參數,你可以通過不超過2個參數performSelector:withObject:withObject:。如果您需要更多參數,則需要使用數組或字典。

id<MyProtocol> myObj = [[MyClass alloc] init]; 
if([myObj respondsToSelector:@selector(aMethod:)]){ 
    NSArray *args = [NSArray arrayWithObjects:@"First arg",[NSNumber numberWithInt:2],[NSNumber numberWithBool:YES],nil]; 
    [myObject performSelector:(@selector(aMethod:) withObject:args]; 
} 

否則,如果您定義一個協議與可選的方法,你只需要調用它正常:

id<MyProtocol> myObj = [[MyClass alloc] init]; 
if([myObj respondsToSelector:@selector(aMethodWithFirstArg:second:third:)]){ 
    [myObject aMethodWithFirstArg:@"First arg" second:2 third:YES]; 
} 

同樣的結果,但我更喜歡第二個與協議!