11

在 Objective-C 块的实现中是否支持在super上调用方法?

当我在super上调用一个方法时,会抛出一个EXC_BAD_ACCESS错误,但是一旦我将这些调用从[super methodToCall]to更改为[self methodToCall]让消息向上移动到响应者链,它就可以正常工作。

在该块所在的类的实例中没有实现-methodToCall,但在超类(即 self 继承的类)中有一个。

我只是想知道为什么在块的实现中调用 super 上的方法首先是一个问题(技术上)的细节,所以我将来可以避免它。我怀疑它与如何在块中捕获变量以及有关堆栈和堆的某些内容有关,但我真的没有具体的想法。

注意:在将块存储在属性中后的几秒钟内调用块实现代码,该属性使用复制,所以我认为块的生命周期没有问题,一切看起来都很好。此外,这只是在 iPhone 设备 (3G) 上崩溃,但在 iPhone 模拟器中没有崩溃。

结果EXC_BAD_ACCESS

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
 if (!error) {
  [super didRetrieveItems];
 } else {
  [super errorRetrievingItems];
 }
}];

工作完美,实现-didRetrieveItems-errorRetrievingItems属于超类。

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {

 if (!error) {
  [self didRetrieveItems];
 } else {
  [self errorRetrievingItems];
 }
}];
4

2 回答 2

11

从技术上讲,这是 Objective-C 运行时的问题,以及调用如何super实际工作的底层机制。基本上,它们捕获作为消息接收者的对象(self在所有情况下)和实现方法的特定版本的类(发生方法实现的类的超类)。因为这种消息发送的很多准备工作发生在编译时,而不是运行时,如果它与块的交互很糟糕,我不会感到惊讶。

self当消息即将发送时,我会检查是否仍然有效。通常,块中引用的任何对象都会自动保留。由于super工作方式有点不同,这可能意味着它self没有像人们预期的那样被保留。检查这一点的一种简单方法是使用super最初编写的调用,并简单地泄漏称为 的对象self,并查看它是否有效。如果这是问题所在,您可能必须self在块内插入一个虚拟引用才能获得自动内存管理。

但是,从最严格的意义上说,我不确定您是否可以永远依赖这种工作方式。尽管块可以捕获当前运行时状态,但是(从 OOP 的角度来看)它们打破封装并调用超类实现并没有真正意义,因为实现方法的层次级别应该对任何外部调用代码都是不透明的。我会尝试找到另一个不依赖于继承层次结构的解决方案。

于 2010-12-12T03:12:00.230 回答
8

结果 EXC_BAD_ACCESS:

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {
 if (!error) {
  [super didRetrieveItems];
 } else {
  [super errorRetrievingItems];
 }
}];

可能是由于编译器中的错误;尝试[self class];在该块中向 self 添加或任何其他方法调用,它可能会起作用。

完美运行,-didRetrieveItems 和 -errorRetrieveItems 的实现在超类中。

[self retrieveItemsForId:idString completionHandler:^(NSError *error) {

 if (!error) {
  [self didRetrieveItems];
 } else {
  [self errorRetrievingItems];
 }
}];

我认为您可能对面向对象编程的基本方面之一感到困惑。你说你的类中没有这些方法的实现,它们只存在于超类中。

由于继承,您的类也有效地响应所述方法调用。只需像上面那样使用self. 它将起作用,并且正是您应该这样做的方式!

于 2010-12-12T03:30:24.633 回答