5

对于需要在我的应用程序中联网的对象,我有一个“互联网感知”基类。所有需要互联网感知的对象都继承自它。正如你可以想象的那样,我分配和释放了很多这些对象。

Internet 感知基类具有以下代码,用于与用于检查 Internet 状态的 Reachability 类进行交互。

#import "Reachability.h"

- (id) init {
    ...
    self.internetReachable = [Reachability reachabilityForInternetConnection];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkNetworkStatus) name:kReachabilityChangedNotification object:nil];
    [self.internetReachable startNotifier];
    ...
  }
- (void)  dealloc
 {
    [self.internetReachable stopNotifier];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
 }

一旦我的应用程序中的互联网状态发生变化,应用程序就会崩溃并出现以下错误:

*** -[Reachability isKindOfClass:]:消息发送到已释放实例 0x1e249a30

我已经打开了僵尸,并将问题追溯到 Reachability.m 中的以下代码行

NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");

不幸的是,除了停止监听 NSNotifcations 和停止通知程序之外,我不确定我的对象还能做些什么来避免这个错误。

任何帮助或建议都会很棒。

谢谢

维生素B

编辑:

好的,所以按照下面答案的建议,我在带有分配的工具中运行它,这就是保留计数历史。

在此处输入图像描述

正如我所怀疑的那样,它是一个我释放的对象,它被 Foundation(即 NSNotifcationCenter)而不是我自己调用。

我的互联网对象有一个指向可达性对象的强指针。当它们被释放时,Reachability 对象也是如此。僵尸是可达性对象。在我的 Internet 对象的 dealloc 中,我调用了 removeObserver,但基础仍在调用已释放的对象。我不明白为什么...

4

3 回答 3

7

Foundation 仍在向 NSNotifcations 发送释放的可达性的原因是因为可达性对象在与创建它的线程不同的线程上被释放,即。可达性不是线程安全的。使用 dispatch_async 回到创建可达性对象的同一个队列已经解决了这个问题。

于 2013-03-21T18:59:36.807 回答
4

当您创建的用于实例化 Reachability 并因此持有对 Reachability 实例的引用的对象在没有(或之前)调用 stopNotifier 的情况下被解除分配时,就会发生这种情况!

解决这个问题非常简单。在您的对象从堆栈中删除之前,您必须调用 stopNotifier 来拆除您的 Reachability 实例。您可以在 dealloc 方法中执行此操作,或者如果它是 viewController,您可以在生命周期方法之一中调用它,例如 viewDidDisappear 等。

这里应该没有必要搞乱线程。考虑一下,当您在 Reachability 上调用 startNotifier 时,这件事通过 Reachability 的设计在后台线程上启动。因此,当您调用 stopNotifier 时,它会为您处理线程。

您不得不与线程混淆的原因与您持有对可达性的引用的对象被释放但仍然是网络更改的注册侦听器这一事实有关,这发生在 startNotifier 中。当网络发生变化时,你猜怎么着,你的对象,虽然仍然注册接收通知,却无处可寻!崩溃的崩溃。stopNotifier 在它死之前注销它,一切都很好。

- (void)dealloc
{ // self.hostReachability is my property holding my Reachability instance
    if (self.hostReachability) {
        [self.hostReachability stopNotifier];
    }
}
于 2014-10-21T16:45:38.660 回答
1

The NSCAssert line is where you first access the deallocated object, but if you want to know more about the object's life cycle you should use Instruments. Use Xcode's Profile tool to run your program in the simulator with the Allocations (but not leaks!) tool. In the allocations tool's launch configuration turn on Enable NSZombie Detection and Record Reference Counts. When you hit the NSCAssert line Instruments should detect the attempt to message the zombie info object and make a note of it. If you look at the detailed information for the zombie info object Instruments will show you its history of reference counts and you should be able to see when it was deallocated.

于 2013-03-21T18:13:59.567 回答