Reputation: 13
I'm trying to communicate with iOS hardware to find if the device is using the built in speakers or not. It all works fine but whenever I use this code I get a memory leak. I'm calling it from my C++ code by putting this in a .mm file for objective-c++. This is my first time trying to use objective-c so I'm struggling to see where the leak might be coming from. I don't even know if it is a problem with this code which I have adapted slightly from stack overflow or something with the iOS API? This code is getting called very frequently as well.
From my little knowledge of objective-c I have tried to release some of the objects within the function but this then causes problems when the audio drivers change settings (sample rate etc.) and causes crashes.
bool Headphones::isHeadsetPluggedIn() {
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription* desc in [route outputs]) {
if ([[desc portType] isEqualToString:AVAudioSessionPortBuiltInSpeaker])
{
return NO;
}
}
return YES;
}
Upvotes: 1
Views: 1272
Reputation: 23438
From
I have tried to release some of the objects within the function
I deduce that you have ARC (automatic reference counting) disabled in your Objective-C++ compilation unit. Unless you have a good reason, this is probably not a great idea, as ARC drastically reduces the chance of leaks. I don't think this is the source of your issue, as I can't spot any methods that might return retained objects at first glance.
A significant number of Objective-C methods your code is calling have return types marked or deduced as autorelease. This works around the problem that without ARC, retaining returned objects would mean the caller couldn't simply use the method call expression in another expression, but would always have to assign the result to a variable in order to be able to subsequently release it, while not retaining it could result in a use-after-free situation. For example,
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
would have to be written as the rather awkward
AVAudioSession* session = [AVAudioSession sharedInstance];
AVAudioSessionRouteDescription* route = [session currentRoute];
[session release];
Hence, autorelease. This means the returned object is retained but placed in the current autorelease pool. This is essentially a stack of object pointers which is later released in batches. The batches are delineated by @autoreleasepool
blocks. In Objective-C code, you usually only need to place these blocks explicitly in loops which operate on very large or very many objects to avoid memory use ballooning, but runloop and other event sources implicitly create a pool that is cleared upon return of the event handler.
As your code is predominantly C++, I suspect you aren't hitting such an implicit pool often enough, especially if you are using a traditional game loop. The solution is to wrap your function in a pool:
bool Headphones::isHeadsetPluggedIn() {
@autoreleasepool
{
AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
for (AVAudioSessionPortDescription* desc in [route outputs]) {
if ([[desc portType] isEqualToString:AVAudioSessionPortBuiltInSpeaker])
{
return NO;
}
}
return YES;
}
}
This means any objects marked for autorelease during the execution of this function will be released upon return.
I suspect this will solve your problem.
Upvotes: 1