5

The issue has been discussed here and here, but I wonder if there is a more solid way to solve this whether you have delegates or not - when a function is called after a delay. At a certain point in a program, at a button push, an object - a CCLayer - is created. That layer creates several objects, some of them at callbacks. That created object layer has a "back" button which destroys it. I am running into a problem when the callbacks, etc are triggered AFTER that object is destructed and try to access objects that don't exist anymore - where the "message sent to deallocated instance 0x258ba480" gives me this good news. How do I avoid that?

1) Is there a way to kill the callbacks (because I obviously don't need them anymore) 2) should/can I test for the existence of these possibly non-existent objects at the callbacks themselves 3) something else?

(My callback is code for checking for an internet connection that I copied from this illustrious website - may it live long and prosper-, using Reachability, and I could solve the problem by simply moving it to the main view and setting a flag on the child view, but I don't want to.)

- (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];

}

Community
  • 1
  • 1
Yazan alhoroub
  • 110
  • 1
  • 6
  • "How to avoid deallocated objects being accessed in callbacks" - pay attention to your memory management. –  Oct 01 '13 at 13:37

2 Answers2

5

There are basically two ways to avoid deallocated delegates from being messaged:

  1. Hold onto the objects you want to message later. That way they won’t get deallocated. This is the case with block callbacks – if a block references some object, the object gets retained until the block ceases to exist. If you message some objects from a block and hit a deallocated object, you must have screwed up the memory management somewhere.

  2. Clear the delegation link before you release the delegate. Nowadays this is usually done using weak, zeroing properties that are automatically set to nil when the referenced object is deallocated. Very convenient. Not your case.

zoul
  • 102,279
  • 44
  • 260
  • 354
  • Sounds great. Because I'm new, i'm not doing things the elegant way. Would 'memory management' in this case be achieved by: 1) set object being accessed to nil when layer is destroyed, 2) check with an "if(object)" at the callback before trying to do something to it? Thanks for your time :) – Yazan alhoroub Oct 01 '13 at 13:55
  • Your code looks like it should work correctly. When the label is referenced by a block, it should be retained by the block and stay in memory while the block does. Do you use ARC? Are you sure you do not over-release the label somewhere? How is the `unreachableBlock` property declared? – zoul Oct 02 '13 at 08:49
  • No ARC. Actually I never release it manually. I just remove the whole CCLayer object which contains it from the parent layer when it's not needed anymore. I just checked the label creation process just in case, and nothing fancy there. Declare after @implementation, create and initialize during [super init] and show if no net. As to the declaration of the property, i never really looked into the Reachability original code... or do you mean the declaration of the testInternetConnection function? - If the latter, i didn't. I only declare the functions that might be used from other classes :) – Yazan alhoroub Oct 02 '13 at 16:33
  • Well, it’s hard to debug memory management without seeing all the code. Try to use a custom subclass for the label, override `-release` or `-dealloc`, place a breakpoint there and see (in the stack trace) when your label is released. Then you can do the same for `-retain` and make sure you’re not over-releasing the label. – zoul Oct 03 '13 at 06:48
3

You might consider several options:

First, you may just check for existence of an object before passing message to it:

if (noNetMessageLabel)
  noNetMessageLabel.visible = true;

But personally I consider that as a bad architecture.

More wise decision, from my point of view, would be move the code of displaying any alert regarding internet connectivity to the model. Create method like this in AppDelegate or in the model:

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

Also you may consider moving internet checking code to the model too.

In the ViewControllers of your app implement listening to this notification.

- (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
   }
}

Thus you have more general and quite versatile approach to handle connectivity issues throughout all your application.

Is there a way to kill the callbacks

It's not possible to cancel block (in your case), but it's possible to cancel NSOperation in NSOperationQueue. But that will require to rewrite your implementation of Reachability.

Artem Abramov
  • 4,701
  • 4
  • 27
  • 38
  • That is "wow". I cannot accept two answers, I've read that just now, but for me this was one double question that killed lots of rabbits, and provided insights into things I really need to learn. Many thanks everyone! – Yazan alhoroub Oct 01 '13 at 14:14
  • Note that the condition in `if (obj) [obj sendMessage]` is completely redundant. In Objective-C it’s legal to send messages to `nil` objects, it just does nothing. – zoul Oct 01 '13 at 16:20