7

I use apple's Reachabiliry class in my non-arc project. And when I run it with instruments to find memory leaks, it referes to Reachability method. Here is the problem:

+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;
{
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);

    WReachability* returnValue = NULL;

    if (reachability != NULL)
    {
        returnValue = [[self alloc] init];
        if (returnValue != NULL)
        {
            returnValue->reachabilityRef = reachability;
            returnValue->localWiFiRef = NO;
        }
    }
    return returnValue;
}

The leaked objects are reachability and returnValue. I understand that SCNetworkReachabilityCreateWithAddress creates a new instance and I must CFRelease it, but it happens right in dealloc!

- (void)dealloc
{
    [self stopNotifier];
    if (reachabilityRef != NULL)
    {
        CFRelease(reachabilityRef);
    }
    [super dealloc];
}

So what can I do to avoid memory leak here?

UPD: Maybe the problem is in how reachability get called? I use this method:

+ (instancetype)reachabilityForInternetConnection;
{
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;

    return [self reachabilityWithAddress:&zeroAddress];
}

Then I called Reachability like this:

[[Reachability reachabilityForInternetConnection] startNotifier];

And don't assign it to any object, just use this line. I've tried to change this calls to something like:

Reachability *reachability = [[Reachability reachabilityForInternetConnection] autorelease];
[reachability startNotifier];

But after this analyzer told me "too many autorelease".

Maria
  • 755
  • 1
  • 11
  • 29

6 Answers6

10

@Alexart answer worked for me but if you want a simplified version, use

+(instancetype)reachabilityWithAddress:(void *)hostAddress
{
   SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
   if (ref)
   {
      id reachability = [[self alloc] initWithReachabilityRef:CFBridgingRetain((__bridge id)ref)];
      CFRelease(ref);
      return reachability;
   }
   return nil;

}

saintjab
  • 1,626
  • 20
  • 31
7

I think better to do it next way:

+ (Reachability*) reachabilityWithHostName: (NSString*) hostName;
{
    Reachability* retVal = NULL;
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
    if(reachability!= NULL)
    {
        retVal= [[self alloc] init];
        if(retVal!= NULL)
        {
            retVal->reachabilityRef = reachability;
            retVal->localWiFiRef = NO;
        }
        else
        {
            CFRelease(reachability);
        }
    }
    return retVal;
}
Brian
  • 14,610
  • 7
  • 35
  • 43
alexart
  • 71
  • 1
  • 1
4

If returnValue equals to NULL reachability object is leaked, you should release it in this case. Also by Cocoa naming convention (https://developer.apple.com/library/ios/documentation/cocoa/conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW1) you must return autoreleased object:

+ (instancetype)reachabilityWithAddress:
{
    ...
     returnValue = [[[self alloc] init] autorelease];

Or rename the method to start for example from new: newReachabilityWithAddress or something like this if you don't want to return an autoreleased object.

Try to run static analyser in Xcode, it can help to spot the problems.

yurish
  • 1,515
  • 1
  • 15
  • 16
  • and analyzer & arc also rely on naming conventions – Daij-Djan Dec 18 '13 at 07:43
  • Daij-Djan, could you explain? – Maria Dec 18 '13 at 07:58
  • Yes, and looks like it works, thanks a lot! The last question is: why does not anyone notice that mistake in reachability? It's pretty famous class, it's also uploaded at Apple's official wep-page (https://developer.apple.com/Library/ios/samplecode/Reachability/Introduction/Intro.html). – Maria Dec 18 '13 at 08:11
  • I don't know how writes sample code in Apple but it frequently contain bugs. So use it only as illustration of main principle and don't copy-paste it blindly into you project. If you desire to correct the bug you can fill bug report at https://bugreport.apple.com/ from your developer account. – yurish Dec 18 '13 at 08:20
  • Download latest Reachability code from apple . This leak is handled in updated code (https://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html) – Muhammad Adnan Dec 11 '15 at 04:43
4

Solution with ARC enabled Reachability class.

  1. Add CFAutorelease(ref) below the issue row.
  2. Remove the CFRelease(self.reachabilityRef) code from dealloc

Updated dealloc

- (void)dealloc {
    [self stopNotifier];

    self.reachableBlock          = nil;
    self.unreachableBlock        = nil;
    self.reachabilityBlock       = nil;
    self.reachabilitySerialQueue = nil;
}

Updated reachabilityWithAddress

+ (instancetype)reachabilityWithAddress:(void *)hostAddress {
    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
    if (ref) {
        id reachability = [[self alloc] initWithReachabilityRef:ref];
        CFAutorelease(ref);
        return reachability;
    }

    return nil;
}

Updated reachabilityWithHostname

+ (instancetype)reachabilityWithHostname:(NSString*)hostname {
    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
    if (ref) {
        id reachability = [[self alloc] initWithReachabilityRef:ref];
        CFAutorelease(ref);
        return reachability;
    }

    return nil;
}
Shamsudheen TK
  • 30,739
  • 9
  • 69
  • 102
1

latest reachability.m seems to require ARC, my app is not using it.

I just turned it on for it:

  1. go to targets\build phases\compile sources

  2. find reachability and double click on it

  3. add -fobjc-arc

memory leak is gone now

Boris Gafurov
  • 1,427
  • 16
  • 28
0

The right fix for the code is as follows in addition to CFRelease in dealloc.

look at the body of the code below. Similar body needs to go into reachabilityWithHostName code as well.

+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress
{
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);

    Reachability* returnValue = NULL;

    if (reachability != NULL)
    {
        returnValue = [[self alloc] init];
        if (returnValue != NULL)
        {
            returnValue->_reachabilityRef = CFRetain(reachability);
            returnValue->_alwaysReturnLocalWiFiStatus = NO;
        }
         CFRelease(reachability);
    }
    return returnValue;
}
HaveNoDisplayName
  • 8,291
  • 106
  • 37
  • 47
Veera
  • 54
  • 1