Search code examples
iosswiftsprite-kitnsnotificationcenter

NSNotificationCenter causing EXC_BAD_ACCESS error with SpriteKit


I'm am using a NSNotificationCenter in an attempt to control a timer in a SpriteKit. The code runs fine when I first enter the SKScene but when I try and re-enter the SKScene I'm getting an EXC_BAD_ACCESS error. I think that this is related to the removeObserver function. I'm not sure when to remove the observer, I tried to do it in the prepareForSegue function with no success. My viewController is as follows:

class JobStartedViewController: UIViewController {



var titleOfJob: String = ""

override func viewDidLoad() {
    super.viewDidLoad()

    let skView = self.view as! SKView

  let scene:SKScene = GameScene.init(size: skView.bounds.size)

    NSNotificationCenter.defaultCenter().postNotificationName("stopTimerNotification", object: nil)
    NSNotificationCenter.defaultCenter().postNotificationName("startTimerNotification", object: nil)

    /* Sprite Kit applies additional optimizations to improve rendering performance */
    skView.ignoresSiblingOrder = true

    /* Set the scale mode to scale to fit the window */
    scene.scaleMode = .AspectFill

    skView.presentScene(scene)
}

and I add my observers to my GameScene.swift as follows:

class GameScene: SKScene {   


override func didMoveToView(view: SKView) {


    NSNotificationCenter.defaultCenter().addObserver(self, selector: "stopTimerInBackground:", name:"stopTimerNotification", object: nil)
     NSNotificationCenter.defaultCenter().addObserver(self, selector: "startTimerInBackground:", name:"startTimerNotification", object: nil)

Solution

  • Here's the likely flow of events:

    1. You present JobStartedViewController, it creates the scene and adds it to the view, triggering didMoveToView(_:) and addition of two observers.
    2. You dismiss the view controller or remove the scene from the SKView. At some point shortly after, there are no more strong references to the scene and it gets deallocated. At this point there are still unsafe references to it in the notification center.
    3. You present another JobStartedViewController or otherwise post the stopTimerNotification notification.
    4. NSNotificationCenter tries performing the selector on the deallocated scene and crashes your app.

    The normal practice when using NSNotificationCenter is to remove your observer in the dealloc method for Objective-C or the deinit method for Swift:

    class GameScene: SKScene {
    
        // ...
    
        deinit {
            NSNotificationCenter.defaultCenter().removeObserver(self)
        }
    }
    

    If you are planning to add and remove that scene from your view multiple times you should also consider removing your observers in willMoveFromView(_:).