2013-03-20 91 views
1

我讀的iOS大書呆子牧場的書,其中一個例子說明了如何觀測添加到NSNotificaionCenter:NSNotification面向對象

[[NSNotificationCenter defaultCenter] addObserver:self 
             selector:@selector(orientationChanged:) 
              name:UIDeviceOrientationDidChangeNotification 
              object:[UIDevice currentDevice]]; 

現在orientationChanged的方向是從張貼在NSNotification對象檢索:

- (void)orientationChanged:(NSNotification *)note { 
    NSLog(@"orientationChanged %d", [[note object] orientation]); 
} 

我的困惑是在這條線:[注對象]取向]

NSNotification的對象返回和id,所以這意味着我們不知道對象的類型是在編譯時UIDevice。但是,我們可以從返回的對象訪問方向,而不會在編譯器中出錯。編譯器如何知道對象類型是UIDevice,訪問方向是否可以接受?

回答

4

編譯器如何知道對象類型是?

它沒有。

它看到它是id。類型id是通用對象類型。因此,如果您發送任何消息,編譯器會接受無錯誤。即使你發送一個不存在的。

這是因爲Objective-C是一種動態語言。方法調用(消息發送),綁定,甚至類型都是在運行時由Objective-C運行時庫推斷出來的。如果你發送一個消息的對象沒有實現(「響應」),那麼將會拋出一個運行時異常,而不是編譯器錯誤。

順便說一句,消息id都返回要麼id(所以最多指針大小的值,如大多數整數可以安全地返回),或編譯器會在所有類中的所有可用的選擇名稱和嘗試將該類型匹配到找到的其中一個選擇器中。

+0

你可以隨便什麼消息沒有發送到類型'id'的對象。它必須是編譯器以前見過的方法。否則,你會得到一個編譯器警告「選擇器XXX沒有已知的實例方法」。試試這個:'id foo = @「stuff」; [id qwerty];'。除非你碰巧有另一個具有'qwerty'方法的類,否則不會編譯。 – rmaddy 2013-03-20 16:09:40

+0

@rmaddy它會編譯,你只會得到一個警告。警告!=錯誤。 – 2013-03-20 16:18:59

+0

我說「編譯器警告」。請記住,在大多數情況下,警告很重要,很可能會導致運行時崩潰。當然有些情況下它會起作用。我想說,對於大多數開發者來說,如果你得到這樣的編譯器警告,它必須被修復或者應用程序會崩潰。 – rmaddy 2013-03-20 16:22:22

2

Objective-C中的消息在運行時動態綁定到方法實現,而不是編譯時。如果你試圖發送一個消息給一個沒有實現方法的對象,那麼它會拋出一個無法識別的選擇器發送到實例異常。

通過向對象發送消息「respondsToSelector:」來響應選擇器,可以通過詢問對象來防止引發此異常。如果此方法返回YES,那麼您可以安全地向其發送消息。在類中實現委託時這很常見,因爲協議中的某些方法可能是可選的。

有關Objective-C的消息是如何運作的,你可以閱讀蘋果的Objective-C運行時編程指南:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html#//apple_ref/doc/uid/TP40008048-CH104-SW1