目标-C 新手问题。给出以下(虚构的)代码:
id mysteryObject = [anotherObject mysteriousMethod];
如何在运行时确定 mysteryObject是什么类?
mysteryObject
您可以使用 isKindOfClass或 isMemberOfClass
isKindOfClass
isMemberOfClass
例如:
if ([foo isMemberOfClass:[NSBar class]])
[mysteryObject class]
将得到类对象。但是,通常您希望执行一些 OOPy 操作,比如检查与某些协议或接口的一致性。
在 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:
@protocol
// MyProtocol.h @protocol MyProtocol - (void)methodInMyProtocol; @end //MyClass.h #import "MyProtocol.h" @interface MyClass <MyProtocol> { } - (void)methodInMyProtocol; @end
您可以像下面这样测试一个实例是否实现了 MyProtocol协议:
MyProtocol
if([mysteryInstance conformsToProtocol:@protocol(MyProtocol)]) { [mysteryInstance methodInMyProtocol]; } else { // ... }
对于那些来自 Java 或 C + + 等静态类型语言的人来说,这种做事方式常常让人感到不舒服。您失去了为您检查类型的编译器。然而,动态类型使得许多事情变得更加容易,包括测试,因为您可以在测试时轻松地用赝品替换实例。因此,动态语言的方法是多测试,少担心类型。你有很好的单元测试覆盖率,不是吗?
如果您确实必须在运行时确定一个实例的类(您可能确实不需要) ,那么您可以使用 -[NSObject isKindOfClass:]来测试一个实例是否是一个类的实例或者它的任何子类或者 -[NSObject isMemberOfClass:]来测试一个实例是否是一个特定类的实例。您可以直接检查 Class对象作为 -[NSObject class]的返回值,并且可以使用 NSStringFromClass([mysteryInstance class])获得实例类的字符串名称。
-[NSObject isKindOfClass:]
-[NSObject isMemberOfClass:]
Class
-[NSObject class]
NSStringFromClass([mysteryInstance class])
当与@协议中定义的方法一起使用时,我发现必须强制转换回 id。
例如,self. listener 是一个 id 数组
如果我这么做..。
for(id<PropertyListener> listener in self.listeners) { if ( [ [ listener class] respondsToSelector:@selector(propertyChanged:propertyName:)]) {
我得到一个错误“没有选择器‘ class’的已知实例方法”。然而,当我将本我从一个本我转换到另一个本我时,它起作用了... ... 为什么我不明白。
[ ((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]; } }