NSSound Memory Leak

Discussion in 'Mac Programming' started by larswik, Aug 21, 2011.

  1. larswik macrumors 68000

    Joined:
    Sep 8, 2006
    #1
    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.
     
  2. LostSoul80 macrumors 68020

    LostSoul80

    Joined:
    Jan 25, 2009
    #2
    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.
     
  3. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #3
    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
     
  4. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #4
    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
     
  5. LostSoul80 macrumors 68020

    LostSoul80

    Joined:
    Jan 25, 2009
    #5
    Instruments is useful because it tells you what's leaking. You just have to click on "Leaks". Does it say NSSound?
     
  6. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #6
    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
     

    Attached Files:

  7. LostSoul80 macrumors 68020

    LostSoul80

    Joined:
    Jan 25, 2009
    #7
    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.
     
  8. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #8
    I will give that a try tonight and see if that solves the problem.

    Thanks,
     
  9. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #9
    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
     
  10. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #10
    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.
     
  11. MSlaw macrumors regular

    Joined:
    Aug 11, 2010
  12. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #12
    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.
     
  13. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #13
    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.
     
  14. Littleodie914 macrumors 68000

    Littleodie914

    Joined:
    Jun 9, 2004
    Location:
    Rochester, NY
    #14
    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.
     
  15. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #15
    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!
     
  16. Littleodie914 macrumors 68000

    Littleodie914

    Joined:
    Jun 9, 2004
    Location:
    Rochester, NY
    #16
    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.
     
  17. LostSoul80 macrumors 68020

    LostSoul80

    Joined:
    Jan 25, 2009
    #17
    That way you're losing your allocated memory! Don't do that!

    No offence intended, but I think you should have studied memory management before even getting to code a hello world program. :)
     
  18. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #18
    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.
     
  19. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #19
    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.
     
  20. Littleodie914 macrumors 68000

    Littleodie914

    Joined:
    Jun 9, 2004
    Location:
    Rochester, NY
    #20
    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
     
  21. larswik thread starter macrumors 68000

    Joined:
    Sep 8, 2006
    #21
    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
     

Share This Page