【问题标题】:Assets singletons and reference cycles资产单例和参考周期
【发布时间】:2019-01-30 17:10:43
【问题描述】:

我目前有一个 Assets 单例类,可让我访问纹理、声音和音乐。作为我的合作伙伴和我正在经历我们项目的内存管理阶段,我们已经意识到我们可能会产生严重的泄漏,并且基于我对 Xcode 工具的使用,我们最大的问题可能集中在这个单例类上。虽然肯定存在其他漏洞,但我们注意到当在地图屏幕和游戏屏幕之间来回移动时,有大约 100 mb 的相当稳定的增长,这似乎对应于我们的 11 个地图资产。在这种情况下,我的问题是:

下面的代码是否会创建一个保留循环,如果是,是否可以通过单例类的存在来管理它,或者我们是否应该打破这个东西。纹理图集分开保存?

func transitionToMapScreen()
    {
        //I hope this isn't necessary eventually, but we were trying to ensure all game textures and emitters were deallocated
        Assets.sharedInstance.deallocateGameAssets()

        gameScene = GameScene()

        Assets.sharedInstance.preloadMap
        {
            [unowned self] in

            let mapScene = MapScreen(fileNamed: "MapScreen")!
            mapScene.preCreate()
            mapScene.scaleMode = self.scaleMode

                // Transition with a fade animation
                let reveal = SKTransition.fade(withDuration: 2.0)

                let fadeMusic = SKAction.run
                {
                    Assets.sharedInstance.bgmTitlePlayer?.setVolume(1.0, fadeDuration: 1.0)

                    Assets.sharedInstance.bgmTitlePlayer?.play()

                    Assets.sharedInstance.bgmGamePlayer?.setVolume(0.0, fadeDuration: 1.0)
                }

                let stopGameMusic = SKAction.run
                {
                    Assets.sharedInstance.bgmGamePlayer?.stop()
                }

                let transitionAction = SKAction.run
                {
                    self.view?.presentScene(mapScene, transition: reveal)
                }

                self.run(SKAction.sequence([SKAction.wait(forDuration: 1.0), fadeMusic, SKAction.group([stopGameMusic, transitionAction])]))

        } // end Assets.sharedInstance.preloadMap completion block*/
    }

根据我对 Swift 中保留周期的了解,这不是创建对 Assets 类的自引用并造成内存泄漏吗?这能解释我们的地图资产在内存中保留的行为吗?如果是这样,管理此问题的正确方法是什么?

【问题讨论】:

  • 如果存在保留周期和泄漏,Instruments 或 Memory Graph 会告诉您。
  • 我不确定,但您的 Assets.sharedInstance 可能会保留自己。
  • matt,我确实看到了与此相关的泄漏,但使用的字节非常小。也许这是我对分配的不熟悉,但这是否表明如果我看到内存使用量急剧上升,那么在 4-5 分钟的游戏会话中使用的
  • 当然,我同意。但问题是我不确定为什么会发生这种情况,因为 SK 有一些令人困惑的行为。特别是,尽管事先解除了这些资产的分配,但一旦所述预加载完成,我看到进入某些场景时不会发生某些预加载。这令人困惑,但在进一步阅读之后,似乎 SK 有时会有意无意地将资产保存在内存中。所以我看到了这种行为,我没有看到与之相关的泄漏,但我确实看到我的纹理图集中的仪器的内存使用量急剧上升。
  • 澄清一下:我目前基于 Instruments 的猜测是,这不是 Assets 单例的保留周期问题(我们顺便消除了 - 显然,每个人都认为单例是邪恶的),而是 SK 在预加载某些资产后有意保留它们。我们有很大的纹理图集,我们正在缩小这些图集以测试该理论。我认为这篇文章对这个话题特别有用:stackoverflow.com/questions/37119707/…

标签: swift memory-management memory-leaks sprite-kit retain-cycle


【解决方案1】:

我想在这里为那些可能正在寻找与寻找保留周期相关的类似问题的答案以解释内存增长问题的人发布此消息。首先,非常感谢所有帮助我阻止我的神经元随机疯狂尝试找到没有保留循环的人(或者没有足够大的保留循环)。现在:

首先,保留周期确实很可怕,但是使用工具来找到它们,然后按照 Apple 的建议进行管理,因为 Swift 4.2 是合适的:

something()
{
    [weak self] in 

    guard let self = self else { return }

    self.whatever()
}

我看到一些人争辩说,您应该确定 unowned 或 weak 是否有意义——老实说,这消除了猜测,而且容易得多。我确实发现至少对我们来说,无主崩溃很少见,但我不会对你的应用发表意见,这可以解决问题。现在,那么,一旦你打扫完房子:

我们发现我们的内存增长问题并非源于我们的资产单例类本身,而是源于我们的纹理图集的绝对大小以及这些图集的相应重叠使用。我不能足够推荐这个讨论:How does SKTexture caching and reuse work in SpriteKit?。这将比我更好地从概念上解释您使用地图集可能面临的问题。

不过,总而言之:SpriteKit 管理您的纹理图集的分配,因此您必须了解,如果您有一个非常大且经常加载的图集,它可能无法按您的预期管理它(我仍然没有足够的细节以更好的方式描述这一点,但正如我所说,请参考上面的讨论以及 Apple 在 SKTextureAtlas 上的开发人员指南:https://developer.apple.com/documentation/spritekit/sktextureatlas)。

现在,与 Apple 讨论相关,我注意到这一行,我认为确实应该用红色粗体显示:“当访问图集的纹理之一时,SpriteKit 会隐式加载图集。”这对于解决我认为的根本问题至关重要:出于某种原因,我们有几个地方通过单个实例访问了图集中的纹理——您必须认识到,在大图集的情况下,SpriteKit 将加载您的整个庞大的地图集进入记忆。因此,我不再轻视 Apple 的注释来管理您的地图集大小。地图集适用于始终一起使用并且将被绘制在一起的资产。将不同的资产加载到图集中是我们的错误。我们正在相应地重新组织我们管理这些的方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 1970-01-01
    • 2018-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多