【问题标题】:Obj-C subclass doesn't seem to recognize inherited selectorObj-C 子类似乎无法识别继承的选择器
【发布时间】:2012-06-15 17:00:06
【问题描述】:

首先,我的代码:

@interface Block : NSObject {
    NSData *data;
    NSInteger slice_count;
}

@property (readonly) NSData *data;

+ (Stopwatch *) runOldTestsUsingConfiguration:(TestConfiguration *)c;

- (Slice *) getSlice:(NSUInteger)idx;

@end

- (Slice *) getSlice:(NSUInteger)idx {
    void *b = (void*)[data bytes] + idx*slice_count;
    int len = [data length] / slice_count;
    Slice *ret = [Slice alloc];
    [ret initWithBytesNoCopy:b length:len freeWhenDone:NO];
    return ret;
    //NSString *temp2 = [data description];
    //NSRange r = NSMakeRange(idx*slice_count, [data length] / slice_count);
    //NSData *d = [data subdataWithRange:r];
    //NSString *temp = [d description];
    //Slice *s = [[Slice alloc] initWithBytesNoCopy:(void *)[d bytes] length:r.length freeWhenDone:NO];
    //return s;
}

其中SliceNSData 的简单子类。

由于某种原因,我收到了一个运行时错误,这似乎表明我的 Slice 实例要么 a) 实际上不是具体实例 (?) 要么 b) 其继承出现问题并且消息不是t 将自身正确绑定到 Slice(几乎可以肯定是我的未知错误)。

我得到的确切错误是:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '***
initialization method -initWithBytes:length:copy:freeWhenDone:bytesAreVM:
cannot be sent to an abstract object of class Slice: Create a concrete instance!'

谁能帮帮我?我已经尝试了几乎所有我能想到的(基本例程在消息调用本身中有详细说明),但我仍然干涸。当它说“创建一个具体实例”时是什么意思?我alloc的时候不就是这样吗?

【问题讨论】:

  • 拆分 alloc/init 是糟糕的 Cocoa 风格,(尤其是在没有重新分配的情况下)并且永远不应该在任何不属于你的课程上完成(因为你不能确保init 不会丢弃它传递的对象并返回一个新对象)。
  • 见下文;那时我并不关心风格。这是我多年来采用的一种调试技术。我知道这样做的危险,但那时我想不到其他的办法。

标签: objective-c inheritance subclass


【解决方案1】:

子类化 NSData 比您想象的要复杂得多。在大多数情况下,最好只写一个 NSData 的包装器而不是一个完整的子类。

【讨论】:

  • 为什么写一个包装器和写一个“完整的子类”会给我不同的结果? OO 语言通常在这些方面非常一致(至少是我真正熟悉的那些,我承认 ObjC 还不是其中的成员)
  • @vermiculus NSData 可能不是您期望的那样,因为它是类集群的一部分。这就是为什么子类化它不能像你期望的那样工作。如果你包装它,那么你就不会暴露 NSData 的实现细节。您可以在this answer 了解更多信息
  • @vermiculus 它的简短版本是,当您调用[[NSData alloc] initWithBytes...]; 时,它会返回一个子类,该子类被认为是NSData 的“具体实现”。如果您真的想对NSData 进行完整的子类化,那么您需要实现其他几种方法才能使其按您期望的方式工作。
  • @DavidRönnqvist,如果我只使用公共方法,这有什么关系?我的意思是,我会尝试这个建议,但如果它有效,我仍然想知道为什么 XD
  • @vermiculus 阅读the answer that I linked to in my previous comment。它解释了为什么它很重要。
【解决方案2】:

IIRC,init 方法允许重新分配自身,因此应始终与 alloc 在同一行使用。

Slice *ret = [[Slice alloc] initWithBytesNoCopy:b length:len freeWhenDone:NO];

我不确定这是否是根本原因,但这对我来说是一个危险信号,可能会引导您朝着正确的方向前进。

编辑:

实际上,我想知道您是否在子类中覆盖了 +alloc 并且没有返回实例...

【讨论】:

  • 这很可能不是问题所在。不过,很高兴指出,但它应该是一个评论。
  • Slice 只引入了一个实例变量和一个方法,不是继承的方法。在绝望的时候,我倾向于撕开表达式并逐行检查它们:P 谢谢,但我也不认为这是问题所在。 :P
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-29
  • 2011-01-12
  • 2020-06-30
相关资源
最近更新 更多