ObjC选择器/块签名更强大,但比预期的类型安全更少? (自动“不安全”铸造)

时间:2013-05-17 15:13:01

标签: objective-c objective-c-blocks

考虑:

[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”是一切的超类(可以这么说吧)。

嗯?评论

3 个答案:

答案 0 :(得分:4)

你的两段代码实际上没有区别。

enumerateObjectsUsingBlock:方法被声明为将块作为参数,其中块参数为(id, NSUInteger, BOOL*)。您可以将它声明为(MyClass*, NSUInteger, BOOL*)的更具体的对象类型,这只是Objective-C的一个特性;只要更具体的类型是某种Objective-C实例引用,它允许更具体的类型说明符替换id参数。< / p>

块是C语言功能是无关紧要的。 C函数也是如此,但您也可以将对象类型传递给C函数。事实上,Objective-C方法实际上只是C函​​数,一开始就有两个参数; self_cmd(在任何方法中尝试NSLog(@"%@", NSStringFromSelector(_cmd));)。

答案 1 :(得分:2)

语法可以是C语法,但类型id是Objective-C类型。你不能在编译为严格C而不是Objective-C的东西中使用那个块签名,除非你为id添加一个typedef来说void*

答案 2 :(得分:-1)

实际上,它与NSObject是所有内容的超类(idNSObject*)的事实有关。由于HBStaffView继承自NSObject,因此您可以将第二个版本视为第一个版本的特化。另一方面,如果原始签名期待NSNumber,并且您尝试使用NSString,则代码将无法编译。在编译时,转换只是“不安全”。如果数组中的对象不是您声明的类型,那么只要您调用该对象的方法,就会在运行时引发异常。

---编辑---

我的立场得到了纠正。正如@Codify所说,id NOT NSObject*,因为它的声明为objc_object *可能暗示。该文件提到

  

id类型定义了一个通用对象指针。可以使用id   声明变量时,但是丢失了编译时的信息   对象。

这可以简单地解释为 ANY 类型的对象。其余解释仍然有效。