2013-05-17 17 views
-1

考慮:ObjC選擇器/塊簽名比預期更強大但類型安全性更低? (自動 「不安全」 鑄造)

[self.staves enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 
    HBStaffView * sv = (HBStaffView *) obj ; 
    [sv flush] ; 
}] ; 

和:

[self.staves enumerateObjectsUsingBlock:^(HBStaffView * sv, NSUInteger idx, BOOL *stop) { 
    [sv flush] ; 
}] ; 

兩個編譯,兩者的工作。

我知道我在枚舉的數組中有什麼,因此編譯器需要將類型轉換爲正確的類型,以便將正確的類型直接傳遞給我,我需要如何使用它顯然是福音。

有趣的是,在這裏,塊簽名是一個「C」語言簽名,與選擇器無關,並且對於對象一無所知(理論上),當然不是「id」是超類一切(可以鬆散地說)。

嗯?註釋?

回答

2

語法可以是C語法,但類型id是Objective-C類型。你不能在那些被編譯爲嚴格C而不是Objective-C的東西中使用該塊簽名,除非,或許你添加了一個typedef爲id來說void*

-1

實際上,它與NSObject是一切事物的超類(idNSObject*)有關。由於HBStaffView繼承自NSObject,因此可以將第二個版本視爲第一個版本的專業化版本。另一方面,如果原始簽名期望NSNumber,並且您嘗試使用NSString,那麼代碼將不會編譯。在編譯時鑄件只是「不安全」的。如果數組中的對象不是您所聲明的類型,那麼只要您調用一個該對象的方法,就會期望在運行時引發異常。

---編輯---

我站好了。正如@Codify所說的,id而不是NSObject*作爲它的聲明objc_object *可能會提示。文檔提及

id類型定義了一個通用對象指針。在聲明變量時可以使用id ,但是會丟失關於對象的編譯時信息。

其中可以簡單地解釋爲ANY類型的對象。其餘解釋仍然有效。

+7

最後一個nitpick:'id'肯定不是NSObject * 。基金會中有更多的基礎課程。 – CodaFi

+2

只需完成CodaFi評論:id是typedef struct objc_object *(請參閱objc/objc.h)。 NSProxy是另一個根類。 – Emmanuel

+5

'id'不僅是'NSObject *',而且兩者甚至都沒有相同的工作。 'id'表示「對任何曾經見過的選擇器做出響應」,而「NSObject *」表示「只響應在'NSObject'上聲明的選擇器。 – bbum

4

你的兩段代碼之間實際上沒有區別。

enumerateObjectsUsingBlock:方法聲明爲將塊作爲參數,其中塊參數爲(id, NSUInteger, BOOL*)。您可以將其聲明爲更具體的對象類型(MyClass*, NSUInteger, BOOL*)只是Objective-C的一個功能;它允許更具體的類型說明符替代id參數,只要該更具體的類型是某種Objective-C實例引用

塊是C語言功能是無關緊要的。 C函數也是如此,但您也可以將對象類型傳遞給C函數。實際上,Objective-C方法實際上只是C函​​數,在開始時總是有兩個參數; self_cmd(以任何方法嘗試NSLog(@"%@", NSStringFromSelector(_cmd));)。