Big memory leak in iOS9 with playSoundFileNamed

I'm posting this because no one seems to discover this problem in iOS9, but it is critical.

The problem is simple, every time you call


sprite.runAction(SKAction.playSoundFileNamed("sound.wav", waitForCompletion: false))

a little bit of memory leaks.

It might not be such a big issue at first, but it will eventually slow down your games since the memory will NEVER be recycled.

Reproducing this bug is easy, just call this method in scene's update function, and you can watch it eating up all your memory.

The solution is simple too, replace the code with the new SKAudioNode for iOS9 and up, and you are good to go.

I Think this is one thing everyone should try first to repair one's game for iOS9.


Replies

I just noticed it as well. Thought I had fixed/worked-around all the ios9 issues.


Funny, all I have to do is keep clicking on a button (click sound effect) and I can use up several MB in seconds.


I've been working through this and using SKAudioNode wherever possible, but because the API is so different, it's not a straightforward switch. Some SFX actions I have are burried deep in sequences and groups of SKActions.


I'll probably leave a few leaks in there, unfortunately.

I agree. Now can anyone elucidate me on the exact fix?

I have been fiddling with SKAudioNode for a couple hours and can't seem to figure out how to exactly replace


sprite.runAction(SKAction.playSoundFileNamed("sound.wav", waitForCompletion: false))


with an SKAudioNode equivalent.


Thanks for any help. ANDY

As the previous poster pointed out the API for SKAudioNode works in a different way from SKAction.playSoundFileNamed: The main difference being that SKAudioNode is an actual node (that has a position etc.) so to get a sound out of it you need to do three things:


  1. Initialise the SKAudioNode with a sound file
  2. Add it to the scene, either directly or as a child
  3. Run an SKAction.play() action on it to trigger it to play


    func testAudioNode() {
        let audioNode = SKAudioNode(fileNamed: "LevelUp")
        audioNode.autoplayLooped = false
        self.addChild(audioNode)
        let playAction = SKAction.play()
        audioNode.runAction(playAction)
    }


I am guessing that if you have a sprite game character that you wanted to add sounds to you could create an SKAudioNode for each sound and then parent them to the character SKSpriteNode. You could then access each sound by searching for its node or if you use a custom SKSpriteNode subclass you could add weak var properties to store pointers to each sound.


    func setupSprite() -> SKSpriteNode {
        let spriteNode = SKSpriteNode(color: SKColor.blueColor(), size: CGSize(width: 50, height: 50))
        spriteNode.position = CGPoint(x: 25, y: 25)
        let audioNode = SKAudioNode(fileNamed: "alert")
        audioNode.name = "ALERT_SOUND"
        audioNode.autoplayLooped = false
        spriteNode.addChild(audioNode)
        self.addChild(spriteNode)
        return spriteNode
    }
  
    func moveSpritePlaySound(sprite: SKSpriteNode) {
        let moveAction = SKAction.moveToY(600, duration: 4)
        let soundAction = SKAction.runBlock { () -> Void in
            if let soundNode = sprite.childNodeWithName("ALERT_SOUND") {
                let playAction = SKAction.play()
                soundNode.runAction(playAction)
            }
        }
        let sequence = SKAction.sequence([moveAction, soundAction])
        sprite.runAction(sequence)
    }


There are probably better ways, but this is essentially how it works.


Gary.

Thanks. Yeah, now I 'get' it....

I was looking for a one on one fix for that specific line of code...

Anyway, you have set me on the path of understanding. LOL

Appreciate it.

Hi Fuzzygoat,


Thanks for pointing that out to folks. SKAudioNode is a much more complicated and powerful node than the playSound SKAction. If you find any problems please don't hesitate to file a radar. Even if communication back isn't stellar we do look at them all.


Cheers

Any news about this ? I guess it is still the issue. The thing is that even SKAudioNode is broken in some situations:


http://stackoverflow.com/questions/35662139/skaudionode-crashes-when-pluggin-in-out-headphones


And SKAudioNode requires deployment target set to 9.0 so, not a great workaround.


Using AVAudioPlayer to play short sounds is not an option as well due to performance reasons ...


Currently, it seems that there is nothing useful for playing short sounds in SpriteKit...


@greven


Any useful tips / ideas / news ?

Same thing happens in iOS 10 beta. Will it ever get fixed?

Hi!


I think I am running into this memory leak too. I am using iOS 10, Swift 3, and SpriteKit. After playing my game for a while, it starts losing the ability to get other resources (sprites, etc). When I turn off the sound effects in my game (through my own config dialog), I don't see the issue. I also noticed that after a while, XCode reports that it can't play the sound because the resource was not found and when it nears a crash, I get "too many files open". My conclusion is that I am running into the same issue reported on this post.


To play sound effects I use: _sprite.runAction(SKAction.playSoundFileNamed("sound.mp3", waitForCompletion: false))


Question 1: Is there a fix for this? If so, how can I get it? This is a huge issue that is holding me up.

Question 2: If no fix, are you stating that we should use the SKAudioNode for simple repeated sound effects? I am using it for the background music.


Thank you in advance,



Craig

Update: I was able to resolve this issue with the following:


1) Updated the code to call the sound action on individual sprites and not on the main Scene. This greatly improved the ability to get resources as time progressed. Doing this, I no longer had missing sprites or other resources, but once in a while, the sound would stop and later recover. I found that this was a different bug that was reported.


2) Updated to XCode 8.2 which was recently released. Apple fixed the issue where sound would cut out and then recover when sound effects were played in quick succession.


Just providing some informatoin to help others.

Thanks!

I believe this is a bug in SpriteKit. I mentioned it to an Apple engineer when i had a fatal error caused by this. work around for me was use the SKAudioNode and only use playSoundFileNamed() on non gameplay screens (menus etc)


I meant to file a radar but didn't get round to it yet.

Looks like Apple does not care of developer exprience for years. Several moths of building the game just stuck at this (4th day already).