Активы синглетонов и эталонные циклы - PullRequest
0 голосов
/ 30 января 2019

В настоящее время у меня есть одноэлементный класс Assets, который предоставляет мне доступ к текстурам, звукам и музыке.Когда мы с моим партнером проходили этап управления памятью в нашем проекте, мы поняли, что у нас может быть серьезная утечка, и на основе моего использования инструментов Xcode наша самая большая проблема может быть сосредоточена вокруг этого синглтон-класса.Хотя, безусловно, присутствуют и другие утечки, мы заметили, что при перемещении между экраном карты и игровым экраном взад и вперед наблюдается довольно устойчивое увеличение на ~ 100 МБ, что, по-видимому, соответствует нашим 11 активам карты.С этим контекстом мой вопрос таков:

Будет ли приведенный ниже код создавать цикл сохранения, и если да, то можно ли управлять им с помощью класса singleton, или мы должны разбить эту штуку на атласы текстур?содержатся отдельно?

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 и утечку памяти?И может ли это объяснить поведение ресурсов нашей карты, сохраняемых в памяти?И если да, то каков правильный метод управления этим?

1 Ответ

0 голосов
/ 01 февраля 2019

Я хотел бы опубликовать это здесь для тех, кто может искать ответ на аналогичную проблему, связанную с поиском циклов сохранения, чтобы объяснить проблемы роста памяти.Во-первых, большое спасибо всем тем, кто помог мне остановить случайные безумные попытки моих нейронов найти удерживающие циклы там, где их не было (или они не были достаточно большими, чтобы иметь значение).Теперь:

Во-первых, удерживать циклы страшно, конечно, но использовать инструменты для их поиска, а затем управлять, как рекомендует Apple, поскольку Swift 4.2 подходит:

something()
{
    [weak self] in 

    guard let self = self else { return }

    self.whatever()
}

Я видел некоторых людейутверждайте, что вы должны определить, имеет ли смысл неподходящий или слабый - это честно снимает догадки и делает это намного проще.Я обнаружил, что неизвестные сбои в любом случае редки, по крайней мере, для нас, но я не буду останавливаться на вашем приложении, и это решает проблемы.Теперь, когда вы очистили дом:

Мы обнаружили, что наши проблемы с ростом памяти связаны не с нашим классом синглтонов активов, а с самим размером текстурных атласов и соответствующим перекрывающимся использованием этих атласов.Я не могу рекомендовать это обсуждение достаточно: Как работает кэширование и повторное использование SKTexture в SpriteKit? .Это концептуально объяснит проблемы, с которыми вы можете столкнуться с атласом лучше, чем я.

Вкратце, однако: SpriteKit управляет распределением ваших атласов текстуры, и, соответственно, вы должны понимать, что если у вас очень большой атлас, которыйпри частой загрузке он может работать не так, как вы ожидаете (у меня все еще недостаточно подробностей, чтобы лучше это описать, но, как я уже сказал, пожалуйста, обратитесь к обсуждению выше, а также к руководству Apple по разработке SKTextureAtlas: https://developer.apple.com/documentation/spritekit/sktextureatlas).

Теперь, в связи с тем обсуждением в Apple, я отметил следующую строку, которая, на мой взгляд, должна быть выделена жирным шрифтом и красным цветом: «SpriteKit неявно загружает атлас при доступе к одной из текстур атласа». Это было важно для решениято, что я думаю, было нашей основной проблемой: у нас было несколько мест, где мы по какой-то причине обращались к текстурам в атласе через один экземпляр - вы должны понимать, что в случае большого атласа SpriteKit будет загружать весь ваш массивный атлас в памятьТак что я больше неСлегка примите к сведению Apple, чтобы управлять размерами вашего атласа.Атласы предназначены для активов, которые всегда используются вместе и собираются вместе.Загрузка разрозненных активов в атлас была нашей ошибкой.Мы реорганизуем то, как мы управляем этим соответствующим образом.

...