swiftcocos2d-iphonespritebuilder

SpriteBuilder Swift code connections nil reference and deallocation issues


I am using Swift (XCode 7.2.1) and SpriteBuilder (1.4.9) to create a game. However I am having some problems with the creating code connections between CCB files and Swift classes. I followed the tutorials online and added a doc root var code connection name in SpriteBuilder and then set up the corresponding property in the class as a private weak var m_myVar: MyClass!. However whenever I try and access the property I get a nil reference. If I remove the weak attribute from the code connection then I can access the property, but I receive deallocation issues when the scene is detroyed, e.g. navigating to a new scene using the CCDirector.shareDirector().replaceScene() method.

In my specific case I have a MainScene in SpriteBuilder with the following node hierarchy within it: CCNode -> CCNodeGradient -> Tile. Where Tile a subclass of CCNode defined in a different CCB file and has the doc root var code connection name of m_levelLabel, which maps to the private weak var m_levelLabel: Tile! property of the MainScene class.

The Tile class has 2 properties m_background of type CCSprite9Slice and m_label of type CCLabelTTF, both of which are mapped to private weak var properties in the Tile Swift class.

When the MainScene loads I attempt to set some properties on the m_levelLabel using the following code:

internal func didLoadFromCCB() {
    m_levelLabel.setValue("2");
}

and Tile.setValue is defined in the Tile class as:

internal func setValue(value: String) {
    m_label.string = value;
}

But when the setValue method executes the m_label property is nil and so the fatal error: unexpectedly found nil while unwrapping an Optional value occurs. This is odd as clearly my code connections in the MainScene class have worked fine as I was able to call m_levelLabel.setValue.

Interestingly if I change the properties in the Tile class to be defined as strong references like:

private var m_label: CCLabelTTF!;

Then the code executes fine and the value of the label is updated when the MainScene loads. However the app now crashes when I try and navigate away from the MainScene with the error malloc: *** error for object 0x7a9f3200: pointer being freed was not allocated which sounds like there is a problem when destroying the, now, strong properties in the Tile class owned by the MainScene.

What could be going on here? What is the correct way to define code connections in Swift? Note I receive no warnings in the console about missing code connection properties.

For reference here is the MainScene and Tile class in their entirety:

Main.swift

import Foundation

internal class MainScene: CCScene {

    internal func didLoadFromCCB() {
        m_levelLabel.setValue("1");
        m_progressLabel.setValue("0%");
    }

    internal func play() {
        let gameplayScene: CCScene = CCBReader.loadAsScene("Gameplay/GameplayScene");
        CCDirector.sharedDirector().replaceScene(gameplayScene);
    }

    internal func levelSelection() {
        print("Pressed level selection");
    }

    internal func progress() {
        print("Pressed progress button");
    }

    private weak var m_playButton: CCButton!;
    private weak var m_levelSelectionButton: CCButton!;
    private weak var m_progressButton: CCButton!;
    private weak var m_levelLabel: Tile!;
    private weak var m_progressLabel: Tile!;
}

Tile.swift

import Foundation

internal class Tile : CCNode {

    internal var value: String? {
        return m_label?.string;
    }

    internal func setValue(value: String) {
        m_label.string = value;
    }

    private weak var m_label: CCLabelTTF!;
    private weak var m_background: CCSprite9Slice!;
}

Solution

  • So it turns out that for the code connections to always work they must have internal accessibility rather than private. E.g.

    internal weak var m_label: CCLabelTTF!;
    

    rather than

    private weak var m_label: CCLabelTTF!;
    

    I'm not sure why private works in some instances, but not in others and it's a shame that I can't properly encapsulate this data. I guess this is either due to a bug in the SpriteBuilder library or a limitation due to the Swift language.