如何避免在回调/ etc中访问被释放的对象?

时间:2013-10-01 13:35:32

标签: ios objective-c

已经讨论了herehere这个问题,但是我想知道在延迟之后调用函数时是否有更强大的方法可以解决这个问题。 在程序中的某个点,按下按钮,会创建一个对象 - 一个CCLayer。该层创建了几个对象,其中一些在回调中。创建的对象层有一个“后退”按钮,可以销毁它。当该对象被破坏并且尝试访问不再存在的对象时触发回调等时遇到问题 - “发送到解除分配的实例0x258ba480的消息”给了我这个好消息。我该如何避免?

1)有没有办法杀死回调(因为我显然不再需要它们了) 2)应该/我可以在回调本身测试这些可能不存在的对象的存在 3)别的什么?

(我的回调是用于检查我从这个着名网站上复制的互联网连接的代码 - 可能它使用可达性而长寿和繁荣 - 我可以通过简单地将其移动到主视图并设置一个来解决问题在子视图上标记,但我不想。)

- (void)testInternetConnection
{
    internetReachableFoo = [Reachability reachabilityWithHostname:@"www.google.com"];

    // Internet is reachable
    internetReachableFoo.reachableBlock = ^(Reachability*reach)
    {
         // Update the UI on the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Yayyy, we have the interwebs!");
            //I do the net stuff here
    });
};

// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
    // Update the UI on the main thread
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"Someone broke the internet :(");
        noNetMessageLabel.visible=true; //<-------this goes kaboom
        noNetFlag=true;

    });
};

[internetReachableFoo startNotifier];

}

2 个答案:

答案 0 :(得分:5)

基本上有两种方法可以避免被解除分配的代理被发送消息:

  1. 保留稍后要发送的对象。这样他们就不会被解除分配。块回调就是这种情况 - 如果一个块引用某个对象,该对象将被保留,直到该块不再存在。如果你从一个块中发送一些对象并点击一个解除分配的对象,你必须搞砸某个地方的内存管理。

  2. 在释放委托之前清除委派链接。现在,这通常使用弱的归零属性来完成,当被引用的对象被释放时,这些属性会自动设置为nil。很方便。不是你的情况。

答案 1 :(得分:3)

您可以考虑以下几个选项:

首先,您可以在将消息传递给它之前检查是否存在对象:

if (noNetMessageLabel)
  noNetMessageLabel.visible = true;

但我个人认为这是一个糟糕的架构。

从我的角度来看,更明智的决定是将显示有关互联网连接的任何警报的代码移动到模型中。 在AppDelegate或模型中创建这样的方法:

- (NSError*)presentConnectivityAlert
{
   if () //any error condition checking appropriate
       [[NSNotificationCenter defaultCenter]
          postNotificationName:@"connectivityAlert"
          object:self
          userInfo:userInfo];
}

您也可以考虑将互联网检查代码移动到模型中。

在您的应用的ViewControllers中,听取此通知。

- (void)viewDidLoad {
    [[NSNotificationCenter defaultCenter] 
      addObserver:self
      selector:@selector(didReceiveRemoteNotification:)                                                  
      name:@"connectivityAlert"
      object:nil];
}

- (void)viewDidUnload {
[[NSNotificationCenter defaultCenter] 
  removeObserver:self
  name:@"connectivityAlert"
  object:nil];
}

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
   if (self.isViewLoaded && self.view.window) {
      //Present the user with alert
   }
}

因此,您可以使用更通用且功能更全面的方法来处理所有应用程序中的连接问题。

  

有没有办法杀死回调

无法取消阻止(在您的情况下),但可以在NSOperationQueue中取消NSOperation。但这需要重写您的Reachability实现。