在Objective-C中,给定一个ID,我如何分辨它指向的对象类型?


67

Objective-C新手问题。给出以下(虚构的)代码:

id mysteryObject = [anotherObject mysteriousMethod];

如何在运行时确定什么类mysteryObject

Answers:


85
[mysteryObject class]

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


94

您可以使用isKindOfClassisMemberOfClass

例如:

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


下面的链接,这些方法的文档,如果你需要复习哪一个检测什么(我没有...)developer.apple.com/library/mac/#documentation/Cocoa/Reference/...
亚当Ritenauer

18
isKindOfClass表示对象从类继承,isMemberOfClass表示完全相同的类
bentytree 2012年

3
更精确一点的@bendytree,isKindOfClass意味着完全相同的类,同时也意味着对象从该类继承
kadam

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])


感谢Barry,它仅用于调试“ atm”样式的atm-我真的很好奇mysteryMethod发出了哪些类。
Justicle

您可以在gdb中使用“ po NSClassFromString([myInstance class]])或仅使用“ po [myInstance class]”。
巴里·沃克

测试选择器比测试协议要好,因为后者会强制类显式实现协议才能工作
user102008 2012年

好解释,谢谢。它改善了我检查类的一些代码,现在更正确地检查了选择器。
mm2001 2012年

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];
    }
}

两点:1。一般情况下,你希望你的协议,从继承NSObject 协议(是的,这是不同的,但相关的,对类)。看这里。这将解决您的-class方法问题。2.我猜您可能想知道类实例是否响应选择器,例如[myObject respondsToSelector:sel],而不是类本身[[myObject class] responds...]
粘土桥2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.