id的对象指针如何实现类型安全?



我有下面的类(选自一个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)

Objective-C中的

类型主要用于编译时安全,您的方法会丢失这些安全。编译器不能警告你类型不匹配,例如,你的数组可以很好地包含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消息。

所以不要那样做!使用显式类型,这样编译器可以提前帮助你。

最新更新