Can't fix Severe Memory Leak from Open AL - iphone

I'm nearing the end of a big iPhone project and whilst checking for memory leaks stumbled on this huge one. I implemented the sound following this tutorial:
http://www.gehacktes.net/2009/03/iphone-programming-part-6-multiple-sounds-with-openal/
Works a charm, a lot of people use it but I get a huge leak a start of the project when sound is initially loaded in. Below are the lines of code that start of the leak:
[[Audio sharedMyOpenAL] loadSoundWithKey:#"music" File:#"Music" Ext:#"wav" Loop:true];
[[Audio sharedMyOpenAL] loadSoundWithKey:#"btnPress" File:#"BtnPress" Ext:#"wav" Loop:false];
[[Audio sharedMyOpenAL] loadSoundWithKey:#"ting1" File:#"GlassTing1" Ext:#"wav" Loop:false];
etc. etc. it loads in 20 sounds altogether. And more specifically in the Audio.m file this chunk of code:
+ (Audio*)sharedMyOpenAL {
#synchronized(self) {
if (sharedMyOpenAL == nil) {
sharedMyOpenAL = [[self alloc] init]; // assignment not done here
}
}
return sharedMyOpenAL;
}
I am unsure how to resolve this and any help on the matter would be greatly appreciated.
Thanks.

Isn’t the “leak” simply the Audio singleton? I am not sure how the leak detection works, but from a certain viewpoint most singletons are leaks, since they only release memory after your application exits.
If this really is the case, then it depends on whether you need to release the memory used by the sounds. The memory usage should not go up, so you don’t have to worry about the “traditional leak” scenario where your application takes more and more memory until it gets killed. The code you are using does not seem to support sound unloading, so that if you want to release the memory, you’ll have to add that code yourself.
And a personal viewpoint: Writing a sound effect engine using a singleton is not a good design. Managing the sounds becomes a pain (this is exactly the problem you are facing), the singleton adds a lot of unnecessary boilerplate code, etc. I see no reason the sounds should not be simple separate objects with their own lifecycle – this is the way I’ve done it in my attempt at an OpenAL SFX engine. Of course, I could be wrong.
Update: I suppose the magic ‘assignment not done here’ is the key. The singleton code is taken from the Apple documentation, but somebody inserted an extra assignment. The sharedFoo method should look like this:
+ (MyGizmoClass*)sharedManager
{
#synchronized(self) {
if (sharedGizmoManager == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedGizmoManager;
}
When you perform the extra assignment to self, you create the leak you are looking for.

Related

AudioServicesAddSystemSoundCompletion callback-method is not called after a few calls

I'm using AudioServicesAddSystemSoundCompletion in my app to detect when sound has finished and then trigger some other action.
For some reason I am getting the following behavior, it works for the first 8 to 12 sounds (that's at least what I tested) and then the callback defined for AudioServicesAddSystemSoundCompletion is not being called anymore.
Here is my code to create the sound:
NSString *soundPath = [[NSBundle mainBundle] pathForResource:[[soundFileName componentsSeparatedByString:#"."]objectAtIndex:0]ofType:#"wav"];
Log2(#"soundFileName: %#", soundFileName);
CFURLRef soundURL = (CFURLRef)[NSURL fileURLWithPath:soundPath];
AudioServicesCreateSystemSoundID(soundURL, &sound);
AudioServicesAddSystemSoundCompletion(sound, nil, nil, playSoundFinished, (void*) self);
to play the sound:
AudioServicesPlaySystemSound(sound);
and to do some stuff when the sound finished playing:
void playSoundFinished (SystemSoundID sound, void *data) {
pf //Typedef for PRETTY_FUNCTION log
AudioServicesRemoveSystemSoundCompletion(sound);
[AppState sharedInstance].b_touchesFREE = TRUE;
if([[AppState sharedInstance].ma_cardsToCheck count] >= 2)
{
[[AppState sharedInstance].vc_spiel pairFound];
}
if (((Card*)data).b_isTurnedFaceUp) {
[AppState sharedInstance].i_cardsTurnedFaceUp --;
}
[(Card*)data release];
}
Has anyone of you any idea why it works the first few times and then stops working?
Thx in advance.
Maverick1st
***** Edit *****
I just found out, that this happens when i try to play the sound a second time.
Could it be, that i forgot to release it somewhere?
But I always thought AudioServicesRemoveSystemSoundCompletion handles the memory management.
***** One more edit *****
So posting this on Stackoverflow made me think a bit deeper about the problem and i got the solution now (at least i think i got it ;)).
Unfortunately i cannot answer the question for myself for the next 7.5 hours so i have to edit the question.
Just for you to better understand my problem.
I'm programming a memory game and every Card is a Class containing its image for front and back and the sound it plays when its turned around.
since i only initialize the sound on creation of the card i was not sure if I should call AudioServicesRemoveSystemSoundCompletion every time the sound ends.
So i just tried it without AudioServicesRemoveSystemSoundCompletion and it works now.
Only thing i am not sure about now is if this could lead to a memory leak or something like that.
But for now it works fine.
If someone could tell me if this is ok regarding the memory use i'd be really happe. :)
Best regards.
Maverick1st
If you create the sound (and set sound completion) only once during the lifetime of the app, it should be ok from memory management standpoint. It is always you create - you release.
However, you should call
AudioServicesRemoveSystemSoundCompletion(sound);
AudioServicesDisposeSystemSoundID(sound);
when you don't need the sound anymore (most probably in the dealloc method of the object where you created (or keep reference to) the sound. Do not change the order of these two, otherways you have a memory leak.
Maybe you would find AppSoundEngine useful. It is simple to use wrapper for SystemSoundID and associated C functions.

Memory leak using (void) alloc

I have seen a similar line of code floating about in Apples code:
(void)[[URLRequest alloc] initializeRequestWithValues:postBody url:verifySession httpHeader:nil delegate:self];
URLRequest is my own custom class. I didn't write this and I think the guy that did just grabbed it from Apple's example. To me this should leak and when I test it I'm pretty sure it leaks 16 bytes. Would it? I know how to fix it if it does but wasn't sure as it was taken from Apple's code.
EDIT: The problem was with the SDK, not the above code. See answer below for further details
Thought I might update this as after further testing and the release of iOS4 it has changed.
The above code doesn't leak and the memory footprint of the App returns to normal even after 200 iterations of the code. The leak did occur in iOS3 but was very small, in iOS4 it has completely disappeared both in simulator and device.
Some might wonder why you would want to implement this code but it works and make sense when dealing with lots of different NSURLConnections throughout your code running simultaneously.
Yes. This is a leak, which can easily be fixed by adding an autorelease:
[[[URLRequest alloc] initializeRequestWithValues:postBody url:verifySession httpHeader:nil delegate:self] autorelease];
Perhaps a better fix would be to create a class function that does this:
#interface URLRequest
{
// ...
}
// ...
+ (void) requestWithValues:/* ... */
// ...
#end
Then you could simply use [URLRequest requestWithValues: /* ... */] without invoking alloc.
Not at all sure what this code is supposed to accomplish. It does appear to break every single convention about initialization methods. What's the point of returning a void pointer from an initialization method? The entire point of an initialization method is to return an object. Where in Apple's code examples did you see this?
Having said that, I don't see why it would leak. Since it doesn't return an object there is nothing to leak external to the method. There might be something internally that leaks.
Edit:
It basically does an NSURLConnection.
Because we are submitting a lot of
forms with a lot of different values
we put it in an external class. All
the delegate methods like
didFailWithError: are in NSURLRequest
and connectionDidFinishLoading just
passes the data to its delegate. So it
doesn't really need to return anything
as it is done through a delegate
method.
Yeah, you need to redesign this. At present, this method is just a disaster waiting to happening. If nothing else, everyone else looking at this code will be utterly confused about what you are doing.
If you have no need to retain the object created, then move its allocation and clean up entirely within a method. Change the method name prefix from "initialize" to something like "setup", "configure", "acquire" etc so the name doesn't imply that it creates and returns and object.
If you need a one shot instance of a particular class, use a class method like Michael Aaron Safyan suggested (again without initialize in the name.) The class method should internally initialize an instance, perform the operations needed, return the data to wherever, then dealloc the instance.
That way, you won't have to worry about leaks and everyone else who may read your code (including yourself months down the road) will immediately understand what the code does.

NSInvocationOperation Memory Problems on iPhone

I've been experiencing memory problems (the app will run for a couple of iterations, then receive low memory warning and finally be terminated) while working with NSInvocationOperation in a method called repeatedly by a NSTimer.
The method will be called every 1/4 of a second and I've narrowed down the source of the problem to the following test lines:
-(void)methodCalledByTimer {
NSInvocationOperation *o = [NSInvocationOperation alloc];
[o release];
}
Uncommenting these two lines (to produce an empty method) will prevent the memory problems from arising. Once they are in, memory usage will increase quite fast and finally the app will be terminated.
Can anybody explain what I'm doing wrong here? Do I have to do anything else to make sure, that the NSInvocationOperation object will be properly released?
Thank you very much in avance for your help.
Kind regards,
Michael.
A possible solution might be to just store your NSInvocationOperation somewhere else instead of creating and releasing one each time methodCalledByTimer is called.
I was having problems with NSCalendar where I would create and release one thousands of times to be used for some date work, but I then just created one calendar attached to the appDelegate and accessed that every time. Fixed a ton of memory leaks, and it's probably better than creating a new object every single time.
I believe the problem lies in how you allocate without initializing. The first of the buggy lines should read:
NSInvocationOperation *o = [[NSInvocationOperation alloc] initWithTarget:yourTarget selector:#selector(yourSelector) object:yourObjectOrNil];
Regarding mjdth's answer, I believe you should not attempt to reuse an invocation operation. From the documentation of NSOperation (the superclass of NSInvoationOperation):
"An operation object is a single-shot object—that is, it executes its task once and cannot be used to execute it again."
Furthermore, no Objective-C object should ever be initialized twice.

How to copy objects, like AVAudioPlayer

I'm rather surprised at how few objects implement NSCopying. This is the third time in two weeks where I have needed to duplicate an existing object, without having to either go to disk and reload, or recreate the object and set its settings.
Now I have to duplicate an AVAudioPlayer sound. Why? I want to play the sound twice, without having the sound.currentTime determine the playback location of the second playback.
I thought it might be easy to do a category or subclass AVAudioPlayer and implement copyWithZone. However, the internals of AVAudioPlayer are hidden and I can't easily copy them.
I am now yearning for the good old BlockMove(&var, &newVar, sizeof(varType));
How best to duplicate an AVAudioPlayer sound or a UIView view?
The reason why many objects don't support NSCopying is because it's not always clear on what a copied object should be, and in particular, the excessively pedantic details of what it means to 'copy an object'.
I would take a pragmatic approach in this case. If you need an instance of AVAudioPlayer duplicated just once, I'd recommend this:
AVAudioPlayer *audioPlayer; // Assumed to be valid.
AVAudioPlayer *copiedPlayer = NULL;
copiedPlayer = [[[AVAudioPlayer allocate] initWithData:audioPlayer.data] autorelease];
This is not meant to be a general solution because it does not handle all cases. It's meant to be an example of how you can over come the problem while relying on some usage specific invariants. The most notable example in which this breaks is if audioPlayer was not initialized with a NSData object, but a url instead. It also makes certain relatively safe assumptions about the mutability of data, and how AVAudioPlayer was coded to handle those cases. (I did not look at the documentation long enough to see if such corner case behaviors are documented).
If this is something you need to do a lot, you can probably lash together a more complicated bit of code that implements NSCopying. Since a quick pass at the documentation turns up only two ways to init the object, via a NSData or via a url, and the instantiated object to be copied provides that information... then the implementation of a non-optimal deep copy NSCopying subclass is left as an exercise for the reader. :)
I saw an Apple sample file, CASound.mm, which has a -copyWithZone:
/* support NSCopying */
- (id)copyWithZone:(NSZone *)zone
{
CASound* copy = NSCopyObject(self, 0, zone);
copy->_impl = NULL;
[copy initWithSound: self];
return copy;
}
looking at initWithSound:
- (CASound*)initWithSound:(CASound *)other
{
CASoundImpl* otherImpl = (CASoundImpl*)other->_impl;
if (otherImpl->_url) {
return [self initWithContentsOfURL: otherImpl->_url];
} else if (otherImpl->_data) {
return [self initWithData: otherImpl->_data];
} else {
return NULL;
}
}
Now, I don't know if Apple likes us messing around with the private fields like _impl, but this basically boils down to the same thing as getting the AVPlayer all over again from the file, and not just making an in-memory copy of the existing sound.
I was actually hoping that maybe NSCopyObject would do the deed, but I see Apple doesn't rely on it for the entire copy.

Learn Obj-C Memory Management [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Where are the best explanations of memory management for iPhone?
I come from a web development background. I'm good at XHTML, CSS, JavaScript, PHP and MySQL, because I use all of those technologies at my day job.
Recently I've been tinkering with Obj-C in Xcode in the evenings and on weekends. I've written code for both the iPhone and Mac OS X, but I can't wrap my head around the practicalities of memory management. I understand the high-level concepts but am unclear how that plays out in implementation. Web developers typically don't have to worry about these sorts of things, so it is pretty new to me.
I've tried adding memory management to my projects, but things usually end up crashing. Any suggestions of how to learn? Any suggestions are appreciated.
On top of the official Apple resources listed in the post arul linked to, here are some other good reads on the topic:
Hold me, use me, free me
CocoaDev.com 's Memory Management Section
And for help debugging memory management problems:
NSZombieEnabled
Rules of thumb
Autorelease
Every time you have [[NSObject alloc] init] you wrap it into an autorelease:
// make sure it gets properly released
// autorelease releases the object at a later time.
NSObject *instance = [[[NSObject alloc] init] autorelease];
Things like this (can't remember the term) are always autoreleased, you should create your classes to correspond to this rule too:
NSString *test = [NSString stringWithFormat:#"%i", 4];
Retain / Release
If you need to store an object for longer than the current method retain it:
[instance retain];
If you don't need it anymore or exchanged it with another object:
[instance release];
You should always have the same amount of retains as releases in your code.
Accessors
Objective-C 2.0 let's you declare properties and writes your accessors for you. For example #property(retain, readwrite) NSString *text; looks something like this:
- (NSString *)text {
return text; // I don't like calling variables _test
}
- (void)setText:(NSString *)newText {
[newText retain];
[text release];
text = newText;
}
init / dealloc
In those methods you should always use [self setVariable:…] like this:
- (id)init {
if (self = [super init]) {
[self setText:#"Lorem ipsum dolor sit amet."];
// …
}
return self;
}
- (void)dealloc {
// make sure text is set to nil and the old value gets released.
[self setText:nil];
}
Garbage Collector
Use the Garbage Collector built into Objective-C 2.0, there's little gain from not using it if you can.
How does this retain / release work anyway?
Every time you allocate an object1, [NSObject alloc], the retain count is set to 1. If this count reaches 0 the object is deleted. [instance retain] increases the count by 1 and [instance release] decreases the count by 1.
1 [instance copy] does allocate a new instance too, and therefore also has a retain count of 1.
Memory management in Cocoa is actually pretty easy thanks to the retain / release paradigm. Start by learning the concept of pointers-- while you don't need to be an expert in C to learn objective-c, understanding pointers is essential. Then read this (or another) guide. Write down the rules if you need to on when you should and shouldn't retain an object, and with a little practice you should "get it" in no time.
Keep in mind you could turn on garbage collection and not worry so much about memory management, but I wouldn't recommend this; even with GC enabled there are still times when you have to understand what's going on behind the scenes.
Read the link that arul provided. Now that you're using a language that has no garbage collection (if you're developing for the iPhone) it's time to start thinking about object life times. Every object you instantiate will now have to be deallocated by someone, probably (possibly) you. Memory management is not an easy subject and the only way to get a handle on this is to practice. Play around with allocating an object and deallocating it. Watch the retain counts grow as you add an object to a collection. Look into Autorelease pools. Essentially, you should know where and when an object gets allocated AND deallocated. On systems with limited memory (such as iphone) you'd want an object to disappear as soon as possible.
My suggestion would be to spend a few days playing around with memory management before you start working on the bulk of your application. Debugging memory issues and struggling with application logic is a bit of a hassle.
One mistake I see often is the use of the autorelease convenience calls when a regular release will do. This only makes life difficult for you because this removes the problem from the call site, while making it very difficult to isolate problems in large codebases.
This also forces you to learn memory management from the outset, which is not fun, but worthwhile because you can generally salvage more of the code you've written.
I used the Memory Management video training course from the Mac Developer Network. Gave me exactly what I needed when I was starting out. It immediately paid benefits when I started having my own memory management problems.