2

I am developing an app using swift. I create a subclass from SCNNode for this:

class Charge: SCNNode {
    static var tagCounter = 0 //to give every charge a unique tag
    static var charges = [Charge]() //to have a pointer that can access all charges
    static var selectedCharge: Charge?  //pointer to selected charge

    override init() {
        super.init()
        super.geometry = Charge.carbonAtom()
        Charge.tagCounter++
        self.chargeTag = Charge.tagCounter
        Charge.charges.append(self)
    }
}

Then after initiating the class several times in ViewController, I want to access them by using Charge.charges. But for some unknown reason, only the last instance is available, and it occupies Charge.charges[0].

I tried to track the object movement by adding property in ViewController var test = [Charge]() and call test.append(charge) every time I initiate charge in ViewController. Then, when I want to access all the charges, Charge.charges loses most of its charges but test does not! Can anyone enlighten me on this? Or is it a bug in Xcode?

n.b. I use debugging tool to track this problem. It turns out that Charge.charges loses its first content as soon as the second initialization is finished, but the first content still exists right after the execution of Charge.charges.append(self)

edit: carbonAtom function

class func carbonAtom() -> SCNGeometry {
    let carbonAtom = SCNSphere(radius: 0.8)
    carbonAtom.firstMaterial!.diffuse.contents = UIColor.redColor()
    carbonAtom.firstMaterial!.specular.contents = UIColor.whiteColor()
    return carbonAtom
}
  • It is due to the way you initialize your static variable. Every time a new instance of the `Charge` class is created, `charges` gets reset to a new array. You provide a type initializer or use `dispatch_once` to initialize only once. See this SO question: http://stackoverflow.com/questions/24137212/initialize-class-method-for-classes-in-swift – Code Different Oct 21 '15 at 14:32
  • @ZoffDino, static properties are not reinitialized for every instance. – Rob Napier Oct 21 '15 at 14:39
  • I found similar results to @t4nhpt. I could not reproduce this in an app. It's worth noting that playgrounds call .clone() on the node as part of the Mirror, which can lead to an exponential number of nodes being created. So I've seen the opposite of the problem you describe, but I can't get a case where .count is smaller than expected. Please try to create a MCVE. http://stackoverflow.com/help/mcve – Rob Napier Oct 21 '15 at 14:42
  • What happen if you keep only 2 lines `super.init()` and `Charge.charges.append(self)` in `init` method? – t4nhpt Oct 21 '15 at 15:11

2 Answers2

3

I have just tested, there is not any Xcode bug.

class Charge: NSObject {
static var tagCounter = 0 //to give every charge a unique tag
    static var charges = [Charge]() //to have a pointer that can access all charges

    override init() {
        super.init()
//        super.geometry = Charge.carbonAtom()
        Charge.tagCounter++
//        self.chargeTag = Charge.tagCounter
        Charge.charges.append(self)
    }
}

Create 3 Changes instance:

for var i = 0; i < 3; i++ {
        _ = Charge()
}

print(Charge.charges.count)

The console prints 3.

Try to check your Charge.carbonAtom() static method. I doubt it clear value of the charges array.

t4nhpt
  • 5,264
  • 4
  • 34
  • 43
  • Thanks for testing. However, I still have the problem here. I give additional details: I instantiate the class by pressing button at the storyboard, and access the Charge.charges by pressing another button. So there is an idle period in between class instantiation. My carbonAtom() method should have no problem tho. I include it now in my question – Ghifari Rahadian Oct 21 '15 at 14:56
0

OKAY GUYS I FOUND IT!

There are infos that I dont provide (I thought it was irrelevant) in Charge class:

override func removeFromParentNode() {
    super.removeFromParentNode()
    for var i = 0; i<Charge.charges.count; i++ {
        let charge = Charge.charges[i]
        if Charge.selectedCharge != nil {
            if charge == Charge.selectedCharge! {
                Charge.charges.removeAtIndex(i)
            }
        }
        break
    }
}

and in ViewController

@IBAction func addCharge(sender: AnyObject) {
    let charge = Charge()
    scene.rootNode.addChildNode(charge) //root of problem
    Charge.selectedCharge = charge
    print(Charge.charges.count)
}

what happen is, in the line scene.rootNode.addChildNode(charge), the method automatically calls removeFromParentNode() from charge. Because of that, the charge pointed by Charge.selectedCharge will be removed from Charge.charges when the second charge is initialized.

I try to search for this info in Apple documentation but no avail. If only they document it more comprehensively :/

Thank you for the help guys :) the divide and conquer method I use to replicate the bug really helps narrowing down the problem