Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apple's reachability memory leak

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".

like image 798
Maria Avatar asked Dec 18 '13 07:12

Maria


3 Answers

@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;

}

like image 65
saintjab Avatar answered Oct 18 '22 15:10

saintjab


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;
}
like image 7
alexart Avatar answered Oct 18 '22 14:10

alexart


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.

like image 4
yurish Avatar answered Oct 18 '22 14:10

yurish