如何解决/处理委托EXC_BAD_ACCESS错误?Obj C



我正在编写一个库(Obj-C for iPhone),我想打包并出售,所以我显然需要在将其上市销售之前解决任何设计问题。我还利用这个库来帮助我开发另一个应用程序。

我的库在很大程度上建立在任务委派的基础上。我的主要功能是启动一个(可能的)长时间运行的流程,当它完成后,我调用类的委托中的Delegate Protocol方法。

这里的一个额外的复杂因素是,我经常将这个任务安排为每30秒左右启动一次。通常,我使用[self performSelector:@selector(someMethod:) withthobject:nil afterDelay:30]而不是使用NSTimer来做到这一点。然后,当委托方法成功返回时,我处理返回的数据并在另外30秒内触发该方法。这让我在方法调用之间有30秒的间隔,而不是从一个调用开始到下一个调用的30秒。(这主要是为了防止调用超过30秒,这是不应该发生的。)

我正在捕捉的错误是,有时,委托回调方法失败与EXC_BAD_ACCESS错误。根据我的调查,自从启动长时间运行的进程以来,类库的委托似乎消失了(被释放/释放)。因此,当它调用[[self Delegate] doSomeDelegateMethod]时,它正在访问一个已释放的对象。

我尝试先检查[[self Delegate] respondsToSelector:@selector(doSomeDelegateMethod)],但即使访问显然也抛出EXC_BAD_ACCESS。

检查[self Delegate] == nil似乎也不是正确的方法。

我认为我已经解决了这个问题,在这个特定的实例中,是当实例化我的对象的视图控制器正在消失(因此在它的方式到垃圾转储),我调用[NSObject cancelPreviousPerformRequestsWithTarget:self]。这显然解决了问题。(这个"修复"是否也表明我的对象"知道"即将到来的调用,并将自己保存在内存中,直到它能够成功地、绝望地发射最后一枪?)

这似乎是把创可贴贴在枪伤上。是的,这似乎阻止了我的应用程序这次崩溃,但我的直觉告诉我,这是一个糟糕的解决方案。

我也考虑过在我的viewWillDisappear:animated:方法中将自定义对象设置为nil,这可能是正确的编码模式,但是客户在处理我的对象时必须如此精确,这似乎是不对的。

真正让我烦恼的是,作为一个库开发人员,我还没有找到一种方法来"封装"我的代码,这样如果用户没有做正确的事情,它就不会为用户抛出异常。基本上,我想要一种方法来拥有我的对象:

  1. 获取请求
  2. 去寻找答案。
  3. 查找答案
  4. 尝试返回答案
  5. 意识到电话那头什么都没有。
  6. 放弃,自己死去。(好吧,所以"自己死"可能不会发生,但你懂的。)

一个有趣的侧面观点:

我防止这种错误发生的一个主要原因是:

我做了以下步骤:

    建立我的图书馆的。h/。m文件。
  1. 生成我的库的。a输出文件。
  2. 导入我的库的。a/.h文件到另一个项目。
  3. 出现上述错误。
  4. 必须仔细阅读应该隐藏在。a文件中的。m文件中的一个代码。

我错过了什么吗?如果它为客户机抛出错误,我真的要冒暴露整个源代码的风险吗?(这只是一个次要问题,但我在这里相当关心!)

谢谢你能提供的任何帮助,帮助我成为一个更好的程序员!

——编辑——

我发现了另一个为什么这很重要的原因。在使用这个库的另一个视图控制器中,我实现了NSTimer策略。如果视图从导航堆栈中弹出(即,在viewWillDisappear:animated:方法中),我将使该计时器失效。因此,在视图消失后,不会再有调用到我的库。

这里有一个问题:如果视图在长时间运行的调用的MIDDLE中消失了怎么办?是的,这很棘手,不太可能做到,但我只是在模拟器上发生了。特别是THIS,这就是为什么我在寻找一个解决方案,让我的代码意识到"嘿,这条管道的另一端没有任何东西",然后优雅地失败。有人知道吗?

谢谢!

解决这个问题有几种方法:

  • 传统的委托方法(UITableViewDelegate)要求在离开之前清除自己的委托身份。这通常是在otherObject.delegate = nil代表的dealloc中完成的。如果不这样做,就是编程错误。这基本上就是你所看到的。当委托和委托具有基本相同的生命周期时,这是常见的模式。

  • 另一种方法是NSURLConnection如何处理它:保留您的委托,直到您完成。这一工作的关键在于NSURLConnection具有自己的生命周期,因此保留循环将自动完成。UITableView不能保留它的委托,因为这几乎总是会创建一个永久的保留循环。如果你的对象存在了一段时间,然后消失了,那么这是有意义的。通常在这里,委托的寿命要比委托短得多,所以retain循环不会伤害任何东西。

任何调用performSelector:withObject:afterDelay:的对象都应该在自己的dealloc中调用cancelPreviousPerformRequestsWithTarget:self。但这与你的委托无关。它应该是对象本身所包含的。我不知道为什么我一直认为这是真的,然后又向自己证明这不是真的。当你调用performSelector:…,你被保留,所以你不能在它触发之前释放。我的旁注,虽然是真的,但与此无关。

旁注 cancelPrevious...在我的经验中真的很贵。如果您必须经常调用cancelPrvious...,我建议您保留自己的一次性NSTimer,并在它触发时重置它以获得相同的效果。performSelector:withObject:afterDelay:只是一个一次性计时器的包装。

我自己回答,因为页面警告我不要在评论中展开讨论…:)

好的,所以看来,我的部分答案是,[self performSelector:withObject:afterDelay:]自动保留我的对象,直到它得到"射击",在这一点上,我猜视图控制器死亡。

所以,现在它明白了为什么我的自定义类在试图将其答案返回给它的委托时试图访问一个释放的对象,这是一个__unsafe_unretained对象,这意味着它可以随意死亡(我认为)。

我现在想要的是一种防止导致错误的方法。在。net中,我有各种各样的错误处理选项来做到这一点,但我无法想到一个故障安全的"保释"在这里。

我试过[[self Delegate] isKindOfClass:...,但不能确定什么样的类委托将是,所以它不会工作。

我也试过[[self Delegate] respondsToSelector:@selector(...)]。我不确定为什么会失败,但是我在这里也得到了EXC_BAD_ACCESS。

我不希望我的客户因为这样一个简单、无辜的错误而使我的产品崩溃。

作为题外话,有谁知道为什么这种失败让我如此容易地访问.m文件的内容,应该隐藏在我的。a文件?我是否错误地构建了我的库?

谢谢!

尝试在dealloc中设置delegate为nil。

的例子:

self.fetchedResultsController.delegate = nil;

我最近经常看到这个问题,通常会解决这个问题。尽管委托应该是弱引用,但有时一些私有实现也会使用它们。

如果我释放,我得到不好的访问,如果我保留,我泄漏

这就是我遇到类似问题的地方。

编辑:当使用ARC时,你仍然可以重写dealloc进行清理,你只是不能调用[super dealloc]或释放任何东西。

最新更新