Memory Leaks in CFStreamCreatePairWithSocketToHost iOS
Asked Answered
I

1

7

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.

Inoculation answered 1/5, 2013 at 14:56 Comment(0)
R
12

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

Ruthie answered 1/5, 2013 at 15:10 Comment(2)
I use automatic reference counting in my project, so I can't use release. Is it done automatically for me?Inoculation
It worked! No memory leaks any more. I think you mean NSInputStream in stead of NSString in your answer?Inoculation

© 2022 - 2024 — McMap. All rights reserved.