Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

larswik

macrumors 68000
Original poster
Sep 8, 2006
1,552
11
I was just talking about Memory Management this morning and now came across this problem. I have a sound that plays when a button is pushed. When I checked it in Instruments if found a memory leak. I found the problem to be the NSSound. The method is long but the problem part is in the beginning. The rest of the method is just getting int values from TextFields and doing math. Also I dragged the audio file into my project. and a pop up came up asking me to add it, which I did.

Code:
- (IBAction)combatDieRoll:(id)sender {
    NSSound *dieRollSoundTwo = [NSSound soundNamed:@"diceRollSound.aif"];
    [dieRollSoundTwo play];
    sleep(1);
    dieRoll = arc4random() %100 +1;
    ....
    ...
    ..
}

I did not NARC (NEW,ALLOC,RETAIN,COPY) so the Object dieRollSoundTwo should have been taken care of by the system as the method exited? It plays fine and I would never have guessed a memory leak existed until I tested it with Instruments.
 
Code:
NSSound *dieRollSoundTwo = [NSSound soundNamed:@"diceRollSound.aif"]; [dieRollSoundTwo play];

This code doesn't cause any memory leak. How did you manage to conclude the responsible was NSSound? Posting a screen shot of Instruments may be helpful.
 
I made this 1 minute video of my screen to better show you. I disable the 2 lines that I use to play the sound and then run Instruments again and the leak is no longer there. There is a 1 sec delay when I push the NSButton because I don't want people to just keep pressing it and to mimic rolling real dice, you have to wait till they stop to see the results.

http://sbtraveler.tv/xptracker/leakTest.mov
 
I've read the API docs, and searched around... I am pretty confused about the behavior of soundNamed: in the case that it creates an NSSound. I suppose this newly created object "must" be autoreleased, but the "i'll give you the object you registered unless you didn't, in which case i'll create a new one" seems helpful but unusual.

Have you created an NSSound elsewhere and used setName to name it, or is the object being created here? If it's being created here, have you tried creating it earlier, retaining it, then grabbing it using the name in this section?

-Lee
 
Instruments is useful because it tells you what's leaking. You just have to click on "Leaks". Does it say NSSound?
 
I do have 3 dice roll buttons that call for the same sound. I had set up in my header file NSSound *dieRollSound and then in the implementation I used it like this
Code:
dieRollSound = [NSSound soundNamed:@"diceRollSound.aif"];
[dieRollSound play];

When I tried to isolate the problem I created a new NSSound Object in the method and called it TWO and played the same audio file.
Code:
NSSound *dieRollSoundTwo = [NSSound soundNamed:@"diceRollSound.aif"];
[dieRollSoundTwo play];

Here is a photo of the leak
 

Attachments

  • leak photo.jpg
    leak photo.jpg
    165.9 KB · Views: 121
I do have 3 dice roll buttons that call for the same sound. I had set up in my header file NSSound *dieRollSound and then in the implementation I used it like this
Code:
dieRollSound = [NSSound soundNamed:@"diceRollSound.aif"];
[dieRollSound play];

Are you saying every time you need to play the sound you're initializing it again? If so, then change your code, putting the initialization in -applicationDidFinishLaunching:, and then just call -play on the object when you want the sound.
 
I will give that a try tonight and see if that solves the problem.

Thanks,
 
I added it to the method awakeFromNib

dieRollSoundTwo = [NSSound soundNamed:mad:"diceRollSound.aif"];

In the header file I added NSSound * dieRollSoundTwo; and it still leaks.

While searching the internet for answers I can across this link and it seems that it might be a bug if it is related to this report

http://code.google.com/p/cocotron/issues/detail?id=702
 
I added it to the method awakeFromNib

dieRollSoundTwo = [NSSound soundNamed:mad:"diceRollSound.aif"];

In the header file I added NSSound * dieRollSoundTwo; and it still leaks.

While searching the internet for answers I can across this link and it seems that it might be a bug if it is related to this report

http://code.google.com/p/cocotron/issues/detail?id=702

I find it unlikely that the cocotron "bug" has anything to do with your issue.
Mostly because it is an open source "reverse" engineered implementation of Apple's Cocoa API's.

I suggest creating a new project and try to get it to leak in a clean environment. I've personally found that helpful when I've though that NSxxx objects are leaking without cause.
 
I don't know what the Static analyzer is? I posted the video above and a still shot of the Leak from Instruments.

That's not a bad idea jared_kipe. I'll try that, a new project with limited code.
 
FWIW, the easy way to find out if and object is being autoreleased is to bracket the problem code with a pool: create a NSAutoreleasePool ahead of what you want to check, then drain it right after what you want to check. Then you can check the pointer to see if it is still valid. This is also useful for finding a problem that occurs at the end of the main runloop (e.g., releasing an autoreleased object by mistake). Bracket large sections of code and close the bracketing inward till you find the error.

What I suspect is happening is that the class method is returning a non-autoreleased object that nothing seems to own. Create an ivar somewhere to assign the pointer to and the leak warning might just go away.
 
I would guess that the

Code:
soundNamed:

method returns an object that is not autoreleased. I would follow the above poster's instructions and try wrapping the method call in an Autorelease Pool.

Also, you're missing out on a lot of information that Instruments could provide you concerning the leak.

If you're up for sending me a ZIP of your project, I'd be more than happy to take a look and help you debug it.
 
I found this link tonight and I got educated on a consistent problem I was creating for myself.
http://stackoverflow.com/questions/...lare-alloc-load-and-dealloc-an-nsmutablearray

I added a bunch of code so I could start saving to a plist and create 5 NSMutableArrays to store things in. When I tested my code to find leaks a bunch more pop'd up, the arrays.

After I created this code to test - testArray = [[NSMutableArray alloc]init];
I would go throughout my code and use this code to add things to the MutableArray.
Code:
testArray = [savedList objectForKey:@"theSkills"];
This new Array caused a memory leak. I then tested this code from the example above that solved his problem.
Code:
 [testArray addObject:[savedList objectForKey:@"theSkills"]];
Now this new testArray was no longer a leak problem. If I understand what I did wrong, the '=' created a new instance that wrote over the old testArray so I could no longer release it, am I correct in assuming that?

I have 800 lines of code to fix now.

Thanks for all your help in trying to help me solve this problem!
 
Now this new testArray was no longer a leak problem. If I understand what I did wrong, the '=' created a new instance that wrote over the old testArray so I could no longer release it, am I correct in assuming that?
Nope. But you're not too far off. :)

Since we only have snippets of your code, it's impossible to reach a definite conclusion as to what's going on. But you *are* having memory management issues, and I would strongly recommend you read Apple's Memory Management Guide. Even if you're going to be using Automatic Reference Counting (ARC) in the future, you need to know how this works.

This piece of code:

Code:
testArray = [savedList objectForKey:@"theSkills"];

Is not creating a new instance of anything. It's just assigning your 'testArray' variable to point to the object that was stored in 'savedList' (I assume 'savedList' is an 'NSDictionary'?). This is dangerous, since when 'savedList' is deallocated, since you didn't retain the object 'testArray' points to, you would experience much worse than a memory leak. (EXC_BAD_ACCESS, most likely.)

This:

Code:
[testArray addObject:[savedList objectForKey:@"theSkills"]];

Is much better. You're actually adding the object to the 'testArray', and the array will automatically retain the object. So it is guaranteed to survive until the 'testArray' object is deallocated.
 
After I created this code to test - testArray = [[NSMutableArray alloc]init];
I would go throughout my code and use this code to add things to the MutableArray.
Code:
testArray = [savedList objectForKey:@"theSkills"];
This new Array caused a memory leak.

That way you're losing your allocated memory! Don't do that!

I have 800 lines of code to fix now.
No offence intended, but I think you should have studied memory management before even getting to code a hello world program. :)
 
I did read the doc's on memory management and go back to it when I do have problems. I look at it as learning to drive a car where you can read about it and understand it in principle. But what you read only syncs in once you get practice behind the wheel. People learning will make dumb mistakes.

That being said. I made a big deal out of nothing. I referenced that array 10 times in all my code. Only once did I make that mistake, every other time I did it correctly. So slowly I am learning to drive the car.
 
I discovered something interesting about my Memory leaking NSSound. I opened it up with Xcode 3.2.6 and ran the Instruments leak test and it did NOT leak. Reopened it with with Xcode 4 and it LEAKED again. I even tried to take ownership of the sound
Code:
dieSound = [[NSSound alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"dieRollSound" ofType:@"aiff"] byReference:NO];
So I could release it myself and it still leaked.

Fine in Xcode 3 and leaks in xCode4. Weird.

I am unsure how to do the test that Sydde recommended with the AutoReleasePool. If anyone has any sample code I could look at to help me set this up I would be thankful. 3 nights now I have been trying to solve it.

Thanks.
 
I discovered something interesting about my Memory leaking NSSound. I opened it up with Xcode 3.2.6 and ran the Instruments leak test and it did NOT leak. Reopened it with with Xcode 4 and it LEAKED again. I even tried to take ownership of the sound
Code:
dieSound = [[NSSound alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"dieRollSound" ofType:@"aiff"] byReference:NO];
So I could release it myself and it still leaked.

Fine in Xcode 3 and leaks in xCode4. Weird.

I am unsure how to do the test that Sydde recommended with the AutoReleasePool. If anyone has any sample code I could look at to help me set this up I would be thankful. 3 nights now I have been trying to solve it.

Thanks.
Wrapping something in an NSAutoreleasePool is very simple, and extremely advantageous if you're allocating large amounts of autoreleased memory in a for/while loop that only needs to be used inside the loop.

Code:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
[NSSound soundNamed:@"diceRollSound.aif"];
[dieRollSound play];
...
[pool drain];

Note that the drain method of NSAutoreleasePool is 'better' than calling release. The difference is discussed here: Garbage Collection - NSAutoreleasePool
 
Thanks for that code snippet. I will try it tonight when I get home. One thing I did notice late last night with the leak I forgot to mention, it leaks all by it's self. I just have to launch Instruments and let it sit for 5 seconds and it starts to leak. No buttons pushed or anything. It said the problem is in my awakeFromNib method with that same code.

Thanks again. I will test tonight!

-Lars
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.