Swift SpriteKit 异步/等待错误:Swifts 新并发和 SpriteKit 的内存错误
我们注意到在 SpriteKit run 函数中使用 async wait 时。
自我保留直到运行操作完成,这可能导致内存泄漏。
Task {
try? await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC)
await controller.startIntro()
print("intro done")
}
如果释放控制器,则在等待未完成之前不会释放内存。
呼叫: scene.removeAllActions()
导致内存泄漏。
以下是项目重现步骤以及可能的解决方法:
https://github.com/maradic/SpriteKitConcurrencyBug
这是 SpriteKit 错误还是我做错了什么?
原始苹果论坛帖子:
We noticed when using async await with SpriteKit run function.
Self is retained until run action is completed which can cause memory leak.
Task {
try? await Task.sleep(nanoseconds: 2 * NSEC_PER_SEC)
await controller.startIntro()
print("intro done")
}
If you deallocate controller, memory won't be released until await is not completed.
calling:scene.removeAllActions()
cause memory leak.
Here is project reproducing steps with possible workarounds:
https://github.com/maradic/SpriteKitConcurrencyBug
Is this SpriteKit bug or am I doing something wrong?
Original apple forum post:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
嗯,我想这里一切正常。这就是
await
的工作原理。它等待其async
朋友完成。你有这样的:这可能会造成潜在的强循环,因为所有变量都隐式保留在该块内。只有当Task完成时,变量才会被释放。如果你更好地观察你的
secondWorkAround()
方法,你会发现通过在前两秒返回,保留周期仍然会发生(好吧,视图控制器将一直处于活动状态,直到SKAction 没有结束)。因为你的行动是有限的,这不是什么大事。但想象一下,如果你有一些永远运行的序列。
这应该用
[weakcontroller] in
来解决,并且在您实现该操作(加上删除特定操作)之后,您将看到控制器在sleep
完成后立即解除分配(在2 秒)。另外,执行诸如
scene.removeAllActions()
之类的操作是行不通的。原因 您要删除在场景上运行的操作,而不是在其节点上运行的操作。因此,当您返回到上一个屏幕时,任务并不会结束。现在你的
firstWorkAround()
,好吧,它相当黑客:)我不会这样做。它加速了场景,因此从startIntro()
方法以异步方式运行的操作结束,任务完成。但我不会那样做,这不是工作的方式。要点是您等待
startIntro()
的操作完成。如果它“自然”完成,或者如果你停止它,就会发生这种情况。合法的方法是找到按键操作并停止它。请注意,您也有类似的东西:
这将递归地遍历每个节点并删除它的操作。您可以在此处阅读更多信息。希望这有点道理,并且有帮助。
Well I guess everything here works normally. That is how
await
works. It waits for itsasync
friend to finish. You have this:This can make potentially a strong cycle cause all variables are implicitly retained inside that block. Only when Task is completed, variables will be released. If You observe better Your
secondWorkAround()
method, You will see that by going back in first two seconds, a retain cycle will still occur (well, view controller will be alive untilSKAction
doesn't end). Cause Your action is finite, thats not a biggie. But imagine if You had some sequence that runs forever.That should be solved with
[weak controller] in
and after You implement that (plus remove the specific action), You will see that controller deallocates immediately aftersleep
is finished (after 2 seconds).Also, doing something like
scene.removeAllActions()
, won't work. Cause You are removing actions that are running on scene, not on its nodes. Thus, task doesn't end when You get back to previous screen.Now Your
firstWorkAround()
, well its quite hackish :) I wouldn't do that. It speeds up a scene, thus action that is run in async way from YourstartIntro()
method gets ended, and task is finished.But I wouldn't go that way, it's not how is meant to work. Point is that You await the action from
startIntro()
to finish. That will happen if it finishes "naturally", or if You stop it. And legit way would be to find action by key and to stop it.Note that You have something like this too:
This will go recursively trough every node and remove actions for it. You can read more here. Hope this makes sense a bit, and it helps.