关于iOS9问题的Spritekit removeFromParent()
我的应用程序根据传入事件动态创建多个SKNodes和SKSpritodes。当我尝试清理过时节点及其包含的子节点时,我使用node.removeFromParent 然而,当我尝试在所有节点层上使用deinit的清理代码时,我立即获得EXC_BAD_访问权限 我认为最安全的版本应该是下面的代码,它严重失败,并且出现了上述异常:关于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
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。您将在控制台上看到节点现在已正确释放。