spritebuilder / SpriteBuilder

Objective-C Game Development Suite
http://www.spritebuilder.com/
Other
740 stars 292 forks source link

Swift Code Connections nil reference and deallocation issues #1606

Open Choc13 opened 8 years ago

Choc13 commented 8 years ago

Hi,

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!;
}