考虑:
[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”是一切的超类(可以这么说吧)。
嗯?评论
答案 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
是所有内容的超类(id
是NSObject*
)的事实有关。由于HBStaffView
继承自NSObject
,因此您可以将第二个版本视为第一个版本的特化。另一方面,如果原始签名期待NSNumber,并且您尝试使用NSString,则代码将无法编译。在编译时,转换只是“不安全”。如果数组中的对象不是您声明的类型,那么只要您调用该对象的方法,就会在运行时引发异常。
---编辑---
我的立场得到了纠正。正如@Codify所说,id
是 NOT NSObject*
,因为它的声明为objc_object *可能暗示。该文件提到
id类型定义了一个通用对象指针。可以使用id 声明变量时,但是丢失了编译时的信息 对象。
这可以简单地解释为 ANY 类型的对象。其余解释仍然有效。