Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
关于iOS9问题的Spritekit removeFromParent()_Ios_Swift_Ios9_Xcode7_Sknode - Fatal编程技术网

关于iOS9问题的Spritekit removeFromParent()

关于iOS9问题的Spritekit removeFromParent(),ios,swift,ios9,xcode7,sknode,Ios,Swift,Ios9,Xcode7,Sknode,我的应用程序根据传入事件动态创建多个SKNodes和SKSpritodes。当我尝试清理过时节点及其包含的子节点时,我使用node.removeFromParent 然而,当我尝试在所有节点层上使用deinit的清理代码时,我立即获得EXC_BAD_访问权限 我认为最安全的版本应该是下面的代码,它严重失败,并且出现了上述异常: deinit { if self.child1.parent!.children.contains(self.child1) { self.child

我的应用程序根据传入事件动态创建多个SKNodes和SKSpritodes。当我尝试清理过时节点及其包含的子节点时,我使用node.removeFromParent

然而,当我尝试在所有节点层上使用deinit的清理代码时,我立即获得EXC_BAD_访问权限

我认为最安全的版本应该是下面的代码,它严重失败,并且出现了上述异常:

deinit {
   if self.child1.parent!.children.contains(self.child1) {
       self.child1.removeFromParent()
   }
}
总而言之:子节点仍然有一个父节点self.child.parent!=无甚至在父母的孩子列表中找到child1都会说:嘿,我还在那里,还有po child1和po child1.parent!显示有效的节点对象。但仍然从父级移除会导致应用程序崩溃

deinit中的日志消息显示,每个对象只调用一次Deinitializer

我能够在一个小项目中复制它。基本的东西应该包含在这两类中:

import SpriteKit

public class ContainerNode : SKNode {

    private let myNode : CustomNode

    override init() {
        self.myNode = CustomNode()
        super.init()
        self.addChild(self.myNode)
    }

        required public init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }

        deinit {
            self.myNode.removeFromParent()
            self.removeFromParent()
        }

    }

    public class CustomNode : SKNode {

        private let containerNode : SKNode
        private let child1 : SKNode

        override public init() {

            self.child1 = SKNode()

            self.containerNode = SKNode()

            super.init()

            self.containerNode.addChild(self.child1)

            self.addChild(self.containerNode)
        }

        required public init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }

        deinit {

            if self.child1.parent!.children.contains(self.child1) {
                self.child1.removeFromParent()  // EXC_BAD_ACCESS here !!
            }

            self.containerNode.removeFromParent()
            self.removeFromParent()
        }

    }

class GameScene: SKScene {

    private var myNodes = [ContainerNode]()

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

        if myNodes.count >= 5 {

            self.removeAllChildren()

            self.myNodes.removeAll()

        }

        for touch in touches {

            let location = touch.locationInNode(self)

            let node = ContainerNode()
            node.position = location

            myNodes.append(node)

            self.addChild(node)

        }
    }
}

class GameViewController: UIViewController {

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        if let scene = GameScene(fileNamed:"GameScene") {

            UIApplication.sharedApplication().idleTimerDisabled = true

            let skView = self.view as! SKView

            skView.multipleTouchEnabled = true;
            scene.scaleMode = .ResizeFill

            skView.presentScene(scene)
        }
    }

    override func shouldAutorotate() -> Bool {
        return true
    }

    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
            return .AllButUpsideDown
        } else {
            return .All
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func prefersStatusBarHidden() -> Bool {
        return true
    }
}
代码可以粘贴到标准的宇宙飞船演示中,它会加载演示的游戏场景文件。在背景上单击6次。前5次单击将节点添加到场景中。在第6次单击时,所有节点都将以编程方式删除,这将导致应用程序崩溃

任何想法都值得赞赏。这是苹果虫还是我错过了什么


XCode 7 GM,在模拟器和IPhone5上崩溃…

首先是这句话

我认为最安全的版本应该是下面的代码,它严重失败,并且出现了上述异常:

deinit {
   if self.child1.parent!.children.contains(self.child1) {
       self.child1.removeFromParent()
   }
}
不应指示有此符号的代码块!:

现在,代码中有一些错误的地方,但基本上是过度移动了节点

1.ContainerNode中的Denit错误 删除节点的父节点时,如果没有对该节点的其他强引用,则会自动删除该节点并释放其内存。 而且,非常重要的是,这种情况反复发生在it儿童身上

因此您的ContainerNode类变为:

public class ContainerNode : SKNode {

    private let myNode : CustomNode

    override init() {
        self.myNode = CustomNode()
        super.init()
        self.addChild(self.myNode)
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

//  deinit {
//      self.myNode.removeFromParent()
//      self.removeFromParent()
//  }
}
2.CustomNode也有同样的问题 请按照以下说明更新您的代码

public class CustomNode : SKNode {

    private let containerNode : SKNode
    private let child1 : SKNode

    override public init() {

        self.child1 = SKNode()

        self.containerNode = SKNode()

        super.init()

        self.containerNode.addChild(self.child1)

        self.addChild(self.containerNode)
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

//  deinit {
//      
//      if self.child1.parent!.children.contains(self.child1) {
//          self.child1.removeFromParent()  // EXC_BAD_ACCESS here !!
//      }
//      
//      self.containerNode.removeFromParent()
//      self.removeFromParent()
//  }

}
3.游戏视图控制器 出于某种原因,您从GameViewController中删除了这一行

skView.showsNodeCount = true
如果你把它添加到你的代码中,让我们在这里说

class GameViewController: UIViewController {

    override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    if let scene = GameScene(fileNamed:"GameScene") {

        UIApplication.sharedApplication().idleTimerDisabled = true

        let skView = self.view as! SKView

        skView.multipleTouchEnabled = true;
        scene.scaleMode = .ResizeFill

        skView.presentScene(scene)

        skView.showsNodeCount = true // <<< add this
    }
}
您将能够在屏幕的右下角看到添加到图形中的节点数。您将看到,每次点击后,会添加4个新节点。 第6次之后,除场景外的所有节点都被删除,然后立即再次添加4个节点


希望这能有所帮助。

我终于找到了坠机的原因

SpriteKit不是线程安全的,我用多线程的方式使用它,基本渲染循环加上一个单独的线程接收网络数据包,在同一线程中更新了SpriteKit的场景图。传入网络数据包的不可预测性导致了零星的、难以重现的崩溃

苹果的Bug报告得到了同样的解释:


不要同时从多个线程更新场景图。

添加了控制器和视图类。游戏场景文件是XCode.W.r.t to 1中宇宙飞船演示的开箱即用的场景文件。你是对的:我想指出的是,父对象确实仍然是活动的和有效的对象,但是removeFromParent仍然因为某些原因而挣扎。如果parent为nil,则条件将失败,而不是从parent中移除,并且很明显不再存在parent。代码中有一些内容不必要地过度复杂。还有这个自我。孩子1。父母!。children.containsself.child1是结果。在SpriteKit中,由于弧的存在,当不再有对节点的引用时,节点会自动释放。你应该依赖这个机制,而不是添加复杂的逻辑:-W.r.t 2你说如果没有更多的参考。。。但是,我对子节点有很强的引用,即节点类myNode、containerNode、child1中的成员。因此,如果在父节点上调用Denit,它会首先通过子节点的Denit从节点树中移除子节点,然后从父节点上的Denit返回后,父节点就消失了。您是对的。当我试图在SKCRenderer中查找一个问题时,整个复杂性增加了,该问题在一个单独的运行循环中因访问错误异常而失败。只需更新我在回答中指出的代码,然后将此方法deinit{debugPrintCustomNode deallocated}添加到CustomNode,并将此deinit{debugPrintContainerNode deallocated}添加到ContainerNode。您将在控制台上看到节点现在已正确释放。