Reachability Classes crashing program - not sure why
Asked Answered
S

3

5

I have an 'internet aware' base class for objects which require networking in my app. All the objects which need to be internet aware inherit from it. As you can imagine I allocate and deallocate a lot of these objects.

The internet aware base class has the following code to interact with the Reachability classes used to check for internet status.

#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];
 }

As soon as there is a change in internet status in my app, the app crashes with the following error:

*** -[Reachability isKindOfClass:]: message sent to deallocated instance 0x1e249a30

Ive turned on zombies and tracked the problem down to the following line of code within Reachability.m

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

Unfortunately, beyond stopping listening for NSNotifcations and stopping the notifier, im not sure what else my objects can do to avoid this error.

Any help or suggestions would be great.

Thanks

Vb

Edit:

OK so following the advice of the answer below I ran it in instruments with allocations and this was the retain count history.

enter image description here

It is as I suspected, its an object that I have deallocated being called by Foundation (ie. NSNotifcationCenter) and not myself.

My internet objects have a strong pointer to a Reachability object. When they are deallocated, so is the Reachability object. The zombie is the Reachability object. In the dealloc of my internet object I have called removeObserver, but foundation is still calling the deallocated object. I can't understand why...

Sacrosanct answered 21/3, 2013 at 17:24 Comment(0)
B
2

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.

Bianka answered 21/3, 2013 at 18:13 Comment(0)
S
7

The reason that Foundation was still sending the deallocated Reachability the NSNotifcations was because the Reachability object was being deallocated on a different thread to the one it was being created on ie. Reachability isn't thread safe. Using dispatch_async back to the same queue that the Reachability object was being created on has solved the problem.

Sacrosanct answered 21/3, 2013 at 18:59 Comment(1)
Aha, glad you figured it out!Bianka
H
4

This happens when the object you made that instantiates Reachability, and hence holds a reference to a Reachability instance, is deallocate without (or before) you call stopNotifier on it!

Solving this is very simple. You must call stopNotifier before your object gets removed from the stack tearing down your Reachability instance with it. You can do this in the dealloc method, or if it's a viewController, you could call it in one of the lifecycle methods such as viewDidDisappear, etc.

There should be no need of messing with threads here. Consider, when you call startNotifier on Reachability this thing starts on a background thread by design of Reachability. So, when you call stopNotifier it takes care of threading for you.

The reason you're having to mess with threads has to do with the fact that your object holding the reference to Reachability got deallocated but was still a registered listener for network changes, which happened with startNotifier. When the network does change, guess what, your object, although still registered to receive notifications, is nowhere to be found! Crashy crash. stopNotifier unregisters it before it dies and everything is good.

- (void)dealloc
{ // self.hostReachability is my property holding my Reachability instance
    if (self.hostReachability) {
        [self.hostReachability stopNotifier];
    }
}
Hercule answered 21/10, 2014 at 16:45 Comment(1)
The above comment was really helpful. I was checking the wifi reachability in separate thread and trying to connect to server. And in between if wifi stops then the app was crashing showing * -[Reachability isKindOfClass:]: message sent to deallocated instance 0x1e249a30. Added Stop notifier and the App is working fine.. ThanksMonophony
B
2

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.

Bianka answered 21/3, 2013 at 18:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.