2011-07-20 27 views
5

下面是一個視圖控制器的實現一個片段:理解Objective-C的範圍問題

- (void)myOtherAwesomeMethod 
{ 
    [self myAwesomeMethod]; // Compile ERROR here: Receiver type for instance message does not declare a method with selector 
} 

- (void)myAwesomeMethod 
{ 
    NSLog(@"%@", @"Calling my awesome method..."); 
} 

- (void)viewDidLoad 
{ 
    [self myAwesomeMethod]; 

    [self myOtherAwesomeMethod]; 
} 

我沒有在我的頭文件中聲明myAwesomeMethod方法,但爲什麼是我可以在viewDidLoad調用myAwesomeMethod,但不在myOtherAwesomeMethod

我知道這個錯誤的解決方案是在我的頭文件中聲明該方法,但我想了解爲什麼會發生這種情況。

+0

你在哪裏調用'-myOtherAwesomeMethod'? – vikingosegundo

+1

我相信這只是警告,不是錯誤? – tia

+0

你也可以只是NSLog字符串,不需要格式化它。 –

回答

11

在C中,規則是:你必須在使用它之前聲明它。

文件從上到下編譯。因此,您的代碼中發生了以下情況:

  1. 編譯器讀取您的類的@interface聲明。既然你說既沒有在頭文件中聲明方法,也沒有在符號表中。

    你的符號表包含:什麼又

  2. 編譯器讀取myAwesomeMethod方法定義。你還沒有聲明它,所以它被添加到符號表中。該方法的主體包含對NSLog的調用,該調用很早以前在Apple提供給您的標題中聲明。

    你的符號表包含:myAwesomeMethod

  3. 編譯器讀取viewDidLoad方法定義;它實際上是在超類的頭文件中聲明的。該方法的主體包含調用myAwesomeMethod,這被發現!它還包含致電myOtherAwesomeMethod的電話。從來沒有聽說過!

    現在,這不是一個錯誤,因爲它仍然可以生成代碼來進行調用。它可以通過你如何使用它來推斷參數類型(在這種情況下,沒有參數)。它不能確定返回類型,但(因爲你不使用返回值並不意味着沒有一個)。因此,假定該調用返回id並生成警告,則繼續。

    你的符號表包含:myAwesomeMethod

  4. 最後,編譯器讀取myOtherAwesomeMethod方法定義。它被添加到符號表中。它編譯了正文,其中包含對錶myAwesomeMethod的調用。一切順利。

    在文件的結尾,你的符號表包含:myAwesomeMethodmyOtherAwesomeMethod

如果你從Java等語言的到來,這可能看起來很可笑,但這是因爲Java的工作方式不同比C在Java中,源代碼是一步編譯和鏈接的;如果沒有可用的源代碼或類文件,則無法編譯引用其他類的類。這可能是一個麻煩,但另一方面,除了定義之外,你從不需要關於聲明。

在C中,編譯和鏈接是不同的步驟。編譯器只生成引用可能在別處定義的符號的目標代碼;它使用聲明來確保它生成正確的代碼。鏈接器負責將所有這些符號與其他庫中的定義(靜態或動態)進行匹配。

在Objective-C中,鏈接器實際上不知道/關心Objective-C消息,因爲在編譯時/鏈接時無法知道對象是否可以響應消息。因此,它將壓降傳遞給運行時,如果沒有方法定義就會引發異常。

+0

謝謝@benzado,我發現訂單是罪魁禍首。我不需要在任何地方聲明它,但方法定義需要在它被調用之前提交。無論如何,我都會接受你的回答,以獲得令人敬畏的解釋。 – pixelfreak

0

我承認我不確定它爲什麼在viewDidLoad中有效。

請注意,錯誤顯示「沒有DECLARE」方法。這是一個暗示,它正在尋找一個聲明(通常在一個頭),即一個函數原型。

但是,沒有什麼說聲明必須在標題中。如果您不打算從文件外部調用該方法,您可以將它放在實現文件的頂部。