在Objective-C中,给定一个id,我该如何判断它指向的对象类型?

时间:2009-07-14 04:52:48

标签: objective-c

Objective-C新手问题。鉴于以下(虚构)代码:

id mysteryObject = [anotherObject mysteriousMethod];

如何在运行时确定哪个类mysteryObject

4 个答案:

答案 0 :(得分:90)

您可以使用isKindOfClassisMemberOfClass

例如:

if ([foo isMemberOfClass:[NSBar class]])

答案 1 :(得分:81)

[mysteryObject class]

将为您提供类对象。但是,通常你想做一些OOPy,比如检查是否符合某些协议或接口。

答案 2 :(得分:53)

在像Objective-C(或Python或Ruby)这样的动态类型语言中,您通常不会想要知道它是什么类型的对象。考虑对象是否响应您希望发送的消息通常会更有效率;如果是这样,你不应该关心它实例化的类,如果没有,你必须处理这个案例而不管实例的类型。这被称为“鸭子打字”......如果它像鸭子一样嘎嘎叫它就是鸭子。

您可以测试对象是否响应特定消息(在Objective-C中称为选择器),如下所示:

if([mysteryInstance respondsToSelector:@selector(messageIWishToSend)]) {
  [mysteryInstance messageIWishToSend];
} else {
  //handle case where instance doesn't respond to the desired message
}

比测试单个选择器更好的是定义一个@protocol来描述您希望用于类的API:

// MyProtocol.h
@protocol MyProtocol
- (void)methodInMyProtocol;
@end

//MyClass.h

#import "MyProtocol.h"

@interface MyClass <MyProtocol> {

}
- (void)methodInMyProtocol;
@end

您可以测试实例是否实现MyProtocol协议,如下所示:

if([mysteryInstance conformsToProtocol:@protocol(MyProtocol)]) {
  [mysteryInstance methodInMyProtocol];
} else {
  // ...
}

对于来自Java或C ++等静态类型语言的人来说,这种做事方式通常会让人感到不舒服。您松开了编译器检查类型。动态类型使得很多事情变得更容易,包括测试,因为您可以在测试时轻松地用假冒替换实例。因此,动态语言方法是测试更多并且更少担心类型。你确实有很好的单元测试覆盖率,不是吗?

如果您确实必须在运行时确定实例的类(并且您可能根本不需要),则可以使用-[NSObject isKindOfClass:]来测试实例是类的实例还是任何实例它的子类或-[NSObject isMemberOfClass:]来测试实例是否是特定类的实例。您可以直接检查Class对象作为-[NSObject class]的返回值,并且可以使用NSStringFromClass([mysteryInstance class])获取实例类的字符串名称。

答案 3 :(得分:1)

我发现当与@protocol中定义的方法一起使用时,我必须转回id。

例如,self.listeners是一个id

的数组

如果我这样做......

for(id<PropertyListener> listener in self.listeners) {        
    if ( [ [ listener class]  respondsToSelector:@selector(propertyChanged:propertyName:)]) {

我收到错误“选择器'类'没有已知的实例方法”。然而,当我将id从id转换为id时,它的工作原理......为什么我不理解。

[ ((id)listener) class] respondsToSelector .... 

这是完整循环...

for(id<PropertyListener> listener in self.listeners) {        
    if ( [ [ ((id)listener) class]  respondsToSelector:@selector(propertyChanged:propertyName:)]) {
        [listener propertyChanged: self propertyName:@"thePropName"];
    } else {
        [listener propertyChanged: self];
    }
}