Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory Leaks in CFStreamCreatePairWithSocketToHost iOS

I use NSInputstream & NSOutputstream to setup a connection and send data. My stream object has a function to open and close the stream. I use the following code:

@interface Stream()
{
    NSInputStream *inputStream;
    NSOutputStream *outputStream;
}

-(id)init
{
    self = [super init];
    if (self)
    {
        inputStream = nil;
        outputStream = nil;
    }
    return self;
}

-(int)streamOpenWithIp:(NSString *)ip withPortNumber:(int)portNumber;
{
        CFReadStreamRef readStream;
        CFWriteStreamRef writeStream;

        CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)ip, portNumber, &readStream, &writeStream);

        if(readStream && writeStream)
        { 
            //Setup inpustream
            inputStream = (__bridge NSInputStream *)readStream;
            [inputStream setDelegate:self];
            [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [inputStream open];

            //Setup outputstream
            outputStream = (__bridge NSOutputStream *)writeStream;
            [outputStream setDelegate:self];
            [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [outputStream open];
        }
}

- (int)streamClose;
{ 
        CFReadStreamSetProperty((__bridge CFReadStreamRef)(inputStream), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
        CFReadStreamSetProperty((__bridge CFReadStreamRef)(outputStream), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);

        //Close and reset inputstream
        [inputStream setDelegate:nil];
        [inputStream close];
        [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        inputStream = nil;

        //Close and reset outputstream
        [outputStream setDelegate:nil];
        [outputStream close];
        [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        outputStream = nil;
}

This code works fine when I open and close the stream many times. When I check my app for memory leaks with Instruments, it said that the function CFStreamCreatePairWithSocketToHost is leaking memory by 72%. Does someone know what I am doing wrong? I can't figure it out.

like image 717
Jim Craane Avatar asked May 01 '13 14:05

Jim Craane


People also ask

Where is memory leak in IOS app?

Xcode's Memory Graph Debugger If you haven't used this yet, it's easy to access while developing. Tapping on the icon will pause your application and generate a graph of the objects with their references to other objects. If there's leaked memory detected, you will see purple icons on the left pane of Xcode.

How memory leak happens in IOS?

As per Apple, a memory leak is:Memory that was allocated at some point, but was never released and is no longer referenced by your app. Since there are no references to it, there's now no way to release it and the memory can't be used again.

How check memory leak in IOS Xcode?

Diagnose the Memory Leak Now, it's time to open the leaks instrument: Choose “Xcode” in the top left of the screen. Expand “Open Developer Tool,” and select “Instruments” Now choose “Leaks,” and make sure you have chosen your target app and device at the top (“Choose a profiling template for…”):

How does swift prevent memory leaks?

Only capture variables as unowned when you can be sure they will be in memory whenever the closure is run, not just because you don't want to work with an optional self . This will help you prevent memory leaks in Swift closures, leading to better app performance.


1 Answers

Add CFRelease((CFStreamRef)inputStream); and CFRelease((CFStreamRef)outputStream); in the streamClose method.

When CFStreamCreatePairWithSocketToHost returns, ownership of readStream and writeStream is passed onto you:

Ownership follows the Create Rule in Memory Management Programming Guide for Core Foundation.

Core Foundation objects need to be explicitly released even when using ARC:

The compiler does not automatically manage the lifetimes of Core Foundation objects; you
must call CFRetain and CFRelease (or the corresponding type-specific variants) as dictated 
by the Core Foundation memory management rules (see Memory Management Programming Guide 
for Core Foundation).

Alternatively, change this line (and corresponding line for outputStream):

inputStream = (__bridge NSInputStream *)readStream;

to:

inputStream = (__bridge_transfer NSInputStream *)readStream;

This is because readStream has an outstanding retain count which ARC is not aware of. By giving ARC the ownership of this pointer, you are giving it permission to release the pointer at appropriate time. Further reading: 1, 2

like image 51
maroux Avatar answered Sep 25 '22 11:09

maroux