【问题标题】:Calling a subclass method on a variable typed as superclass在类型为超类的变量上调用子类方法
【发布时间】:2011-12-13 17:08:57
【问题描述】:

我创建了一个名为Foo的类:

@interface Foo:NSObject{
     int myInt;
}
@property int myInt;
@end

还有一个名为 BarFoo 的子类:

@interface Bar:Foo{
     NSString *myString;
}
@property (copy) NSString *myString;
@end

我正在尝试将Bar 作为Foo 对象存储在数组中,如下所示:

-(void)createBar{
     Foo *object = [[Bar alloc]init];

     // myArray is an instance of NSMutableArray
     [myArray addObject:object];
}

我这样做是因为我实际上有不止一个Foo 的子类(我不想全部列出)。当我从数组中抓取一个对象并将消息发送到该对象以获取myString 变量时,应用程序不执行任何操作。 示例:

-(NSString *)getStringFromFooAtIndex(NSUInteger)index{
     Foo *object = [myArray objectAtIndex:index];

     return [object myString];
}

我是否误解了“消息”的工作原理?我假设我可以向一个对象发送消息,并且无论它是否存在,它都会调用它。我需要以其他方式这样做吗?该数组将保存所有不同类型的Foo 子类,我需要将它们存储在那里。

【问题讨论】:

  • 你@synthesize 你的属性了吗?
  • 是的,在实现文件中。
  • “应用程序什么都不做”是什么意思?它必须返回 something 或抛出异常。

标签: objective-c types subclassing message-passing


【解决方案1】:

我假设我可以向一个对象发送消息,无论它是否存在,它都会调用它。

您确实可以向任何对象发送任何消息;这就是 Objective-C 的乐趣所在。 变量的类型(Foo *Bar *id 或其他)对消息发送没有影响。变量指向的对象知道它的类。相应方法的查找是在运行时通过该类完成的。编译器将括号中的表达式转换为对函数的调用,objc_msgSend

您应该在构建时收到关于[object myString] 的警告,说“'Foo' 可能无法响应'myString'”——编译器知道某处至少有一个类具有对应于myString 的方法,并且它知道,在编译时,Foo 似乎不是其中之一,但它不能保证 Foo 将无法在运行时对消息执行某些操作。消息可以解析in custom ways during runtime。请注意,如果您将变量的类型更改为id,警告就会消失——编译器无法再推断出哪些方法可用。

如果您发送myString 的对象没有响应(即,如果对象真的是 Foo 而不是BarFoo 的子类之一没有实现 myString),将引发异常。这是对无法识别消息的默认响应(通过继承自 NSObject 的任何内容)。

如果您需要向其发送消息的对象的异构集合,您可能需要先测试每个对象。您可以按照 jstevenco 的建议进行操作,并测试功能:

if( [object respondsToSelector:@selector(myString)] ){

或测试身份:

if( [object isKindOfClass:[Bar class]] ){

如果对象是BarBar 的任何子类,则后者将通过。使用isMemberOfClass: 仅测试您指定的类。

【讨论】:

  • 看来我的问题不在于数组或消息,而是我将“Bar”插入到 NSArrayController(我们称之为 arrayController)。如果我正常初始化 Bar 并将其添加到代码中的“myArray”中,我可以整天向它发送消息并且它会工作。如果我使用[arrayController add:bar];,我会丢失与 Bar 相关的任何内容,而只会获取 Foo 的属性和方法。这是正常的吗?谢谢
  • 我不明白你所说的“失去任何相关的东西”是什么意思。您能否具体说明消息发送的结果是什么?您是在谈论 Xcode 中的代码完成吗?不要误以为发生了什么——分析器知道的和编译器一样多,可能更少。
  • 好吧,让我看看能不能更好地解释一下。我在 .xib 文件中放置了一个 NSArrayController (arrayController)。这链接到“App Delegate”对象的 myArray(我正在使用的 NSMutableArray)。 arrayController 将其“模式”设置为类,并将“类名称”设置为 Foo。如果我向其中添加一个 Bar 对象,它会将其视为 Foo 的子类,但它似乎正在从我放置在其中的任何对象中删除所有未在 Foo.h 实现文件中定义的属性。是不是更清楚了。如果您无法理解我要说的内容,我深表歉意……还是有点新意,哈哈。
  • 什么结果让你觉得属性被“丢弃”了?数组控制器的“类名”设置为Foo 只会影响 new 对象的类型,由数组控制器本身创建。它不能更改您插入到数组中的对象的类别。
  • 首先,如果我使用绑定将 NSTextField 绑定到“数组控制器”选择并告诉它使用“myString”值,则会引发异常。其次,当我使用胶水代码(我认为这就是所谓的......)时,当我像这样将项目从数组中拉出时:Foo *object = [myArray objectAtIndex:0] 它没有属性。我使用断点检查了“对象”的属性,但我找不到 myString,即使我在创建它时已将其定义为“Bar”对象。
【解决方案2】:

请参阅here,了解有关如何处理消息传递逻辑的完整摘要。如果不覆盖-(void)doesNotRecognizeSelector:(SEL)aSelector,最终会产生错误。

如果您要拥有一个多态对象集合,您可能应该首先使用respondsToSelector: 测试该对象,然后根据您的需要直接调用该方法或使用performSelector: 变体之一。另一种可能性是在不执行任何操作的基类中包含myString 的实现。

【讨论】:

    【解决方案3】:

    我发现了问题所在。

    在我的 AppDelegate.m 文件中,我有以下方法:

    - (void)createFoo{
         Foo *foo = [[Bar alloc]init];
    
         [myArrayController add:foo];
    }
    

    这是将“foo”对象插入到我的数组控制器中。我也定义了这个方法:

    - (void) insertObject:(Foo *)object intoMyArrayControllerAtIndex:(NSUInteger)index{
         // handles the inserting of the object as well as working with the undo manager
    }
    

    现在,调用[myArrayController add:foo] 是导致问题的原因。如果我将该行替换为[self insertObect:foo intoMyArrayControllerAtIndex:0],那么一切都会完美运行。

    非常感谢您,很抱歉浪费您的时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-19
      • 2018-11-17
      • 2011-04-20
      • 1970-01-01
      • 1970-01-01
      • 2012-04-21
      • 2019-07-18
      • 1970-01-01
      相关资源
      最近更新 更多