我有下面的类(选自一个Apple的例子):
@interface LeTemperatureAlarmService : NSObject <CBPeripheralDelegate>
@property (readonly) CBPeripheral *servicePeripheral;
@end
在另一个类的方法中,我使用以下代码:
NSMutableArray *connectedServices = [[NSMutableArray alloc] init];
... // Adding some myService objects to connectedServices
for (id service in connectedServices) {
if ([service servicePeripheral] == parameter) {
...
}
}
现在让我疯狂的是我可以把servicePeripheral
发送到service
的部分。
据我所知id
是如何工作的,它基本上是一个指针,可以从字面上指向任何对象。我的NSMutableArray
是一个无类型数组,它可以容纳任何类型的对象,甚至混合,所以我不必小心我放进去的东西。
那么,即使我从未指定服务类型,我怎么可能使用[service servicePeripheral]
呢?Xcode是如何知道并在代码补全中建议这个方法的?
Objective-C在方法调用方面与c++不同。编译器不需要知道,因为它不是在编译时完成的,方法是在运行时调用的。具体来说,方法被发送给对象,而不是调用对象。您将servicePeripheral
方法发送给对象,运行时负责调用正确的函数。这也使得您可以将方法发送到nil
而不会崩溃(它将返回false/0/NULL
)
类型主要用于编译时安全,您的方法会丢失这些安全。编译器不能警告你类型不匹配,例如,你的数组可以很好地包含NSString
实例或任何东西,编译器不能帮助你,因为你告诉它你期望id
(也就是任何东西,真的)和servicePeripheral
是一个完全有效和已知的方法。您可以通过在运行时使用isKindOfClass:
检查对象的类来增加类型安全性,例如:
for (id service in connectedServices) {
if ([service isKindOfClass:[LeTemperatureAlarmService class]] && [service servicePeripheral] == parameter) {
...
}
}
那么,即使我从未指定服务类型,我怎么可能使用
[service servicePeripheral]
呢?
确实是,因为您将service
声明为id
。这告诉编译器关闭所有静态类型检查,并允许您向service
发送任何消息。这就是id
的意义:它是通用的接受者(任何消息都可以发送给它,任何对象值都可以分配给它)和通用的供体(它可以分配给任何对象变量)。
你完全有理由对此保持警惕,因为它可能会导致你以后崩溃。它是而不是(正如你的问题标题所示)"类型安全"。这是类型不安全!编译器会很高兴地让你说(例如)[service count]
(因为service
的类型是id
),但是当应用程序运行时,你会崩溃,因为这个对象不响应count
消息。
所以不要那样做!使用显式类型,这样编译器可以提前帮助你。