在Objective-C中,给定一个id,我怎样才能知道它指向的是什么types的对象?

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

id mysteryObject = [anotherObject mysteriousMethod]; 

我怎样才能在运行时确定mysteryObject是什么类?

 [mysteryObject class] 

会得到你的类对象。 但是,通常你想做一些OOPy检查是否符合某些协议或接口。

您可以使用isKindOfClassisMemberOfClass

例如:

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

在像Objective-C(或Python或Ruby)这样的dynamictypes语言中,你通常不想知道它是什么types的对象。 考虑对象是否响应你希望发送的消息往往更有成效; 如果是这样,你不应该关心它实例化了什么类,如果不是,你必须处理案例,不pipe实例的types。 这就是所谓的“鸭打字”…如果它像一只鸭子呱呱叫,它是一只鸭子。

您可以testing某个对象是否响应特定的消息(在Objective-C中称为select器),如下所示:

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

比个别select器testing更好的是定义一个@protocol来描述你想要用于你的类的API:

 // MyProtocol.h @protocol MyProtocol - (void)methodInMyProtocol; @end //MyClass.h #import "MyProtocol.h" @interface MyClass <MyProtocol> { } - (void)methodInMyProtocol; @end 

您可以testing一个实例是否像这样实现MyProtocol协议:

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

对于像Java或C ++这样的静态types语言的人来说,这种做事的方式往往是不舒服的。 你松开编译器检查你的types。 dynamictypes使得很多事情变得更容易,包括testing,因为在testing时你可以很容易地用一个假的代替实例。 因此,dynamic语言的方法是testing更多,并担心typesless。 你有很好的unit testing覆盖率,不是吗?

如果你真的必须在运行时确定一个实例的类(而你实际上可能不需要),你可以使用-[NSObject isKindOfClass:]来testing一个实例是一个类的实例还是它的任何一个子类或者-[NSObject isMemberOfClass:]来testing一个实例是否是一个特定类的实例。 您可以直接将Class对象视为-[NSObject class]的返回值,您可以使用NSStringFromClass([mysteryInstance class])获取实例类的string名称。

我发现在使用@protocol中定义的方法时,必须将其重新转换为id。

例如,self.listeners是一个id数组

如果我这样做…

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

我得到一个错误“没有已知的select器类的实例方法”。 然而,当我把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]; } }