Подтвердить что ты не робот

IOS 7 Sprite Kit освобождает память

Я создаю iOS-игру, нацеленную на новый iOS 7 и Sprite Kit, используя узлы-эмиттеры и физику для улучшения игрового процесса. При разработке приложения у меня возникла серьезная проблема: вы создаете свои сцены, узлы, эффекты, но когда вы закончите и вам нужно вернуться на главный экран, как вы освободите всю память, выделенную этими ресурсами?

В идеале ARC должна освобождать все, и приложение должно вернуться к уровню потребления памяти, который он имел до создания сцены, но это не то, что происходит.

Я добавил следующий код, как метод dealloc представления, который рисует сцену и отвечает за удаление всего при закрытии (удалении):

- (void) dealloc
{
    if (scene != nil)
    {
        [scene setPaused:YES];

        [scene removeAllActions];
        [scene removeAllChildren];

        scene = nil;

        [((SKView *)sceneView) presentScene:nil];

        sceneView = nil;
    }
}
  • sceneView - это UIView, который является контейнером сцены.
  • сцена является расширением класса SKScene, создавая все объекты SKSpriteNode

Я бы очень признателен за любую помощь в этом вопросе.

4b9b3361

Ответ 1

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

Базовая память, выделенная + textureWithImageNamed: может быть или не быть (как правило, не удалена) при переключении на новый SKScene. Вы не можете полагаться на это. iOS освобождает память, кэшированную + textureWithImageNamed: или + imageNamed: когда она сочтет нужным, например, когда она обнаруживает условие с низкой памятью.

Если вы хотите, чтобы память была выпущена, как только вы сделали с текстурами, вы должны избегать использования + textureWithImageNamed:/+ imageNamed:. Альтернативой созданию SKTextures является: сначала создать UIImages с + imageWithContentsOfFile:, а затем создать SKTextures из результирующих объектов UIImage, вызвав SKTexture/+ textureWithImage: (UIImage *).

Я не знаю, поможет ли это здесь.

Ответ 2

Весь этот код лишний. Если у вас нет утечек памяти или удержите циклы в вашем коде, как только вы освободите представление Sprite Kit, все будет очищено из памяти.

Под капотом Sprite Kit использует механизм кэширования, но мы не можем его контролировать, и нам не нужно, если он будет правильно реализован (что можно с уверенностью предположить).

Если это не то, что вы видите в "Инструменты", проверьте наличие циклов, утечек. Убедитесь, что dealloc сцены и просмотра вызваны. Убедитесь, что никакие ссылки на просмотр, сцену или другие узлы не остаются в других объектах (в частности, одиночных и глобальных переменных).

Ответ 3

После борьбы с ним в течение пары дней ключ был фактически:   [sceneView presentScene: nil]; Или для Swift:   sceneView.presentScene(ноль)

который можно сделать в viewDidDisappear. Без этого ваше мнение будет зависеть от сцены для дорогой жизни, даже после того, как ее уволят, и продолжать пережевывать память.

Ответ 4

Swift 3:

В моем личном опыте я решил с помощью инструментов Xcode, в первую очередь с монитором активности, который сразу показал мне огромное увеличение памяти, чем с распределением и утечками.

Но есть и полезный способ: консоль отладки с

deinit {
       print("\n THE SCENE \(type(of:self)) WAS REMOVED FROM MEMORY (DEINIT) \n")
}

Это еще одна помощь, чтобы увидеть, был ли deinit вызывается каждый раз, когда вы хотите удалить сцену.

У вас никогда не будет сильных ссылок на scene или parent в ваших классах, если у вас есть кто-то, у кого вы должны преобразовать его в слабые, например:

weak var parentScene:SKScene?

То же самое для протокола, вы можете объявить его слабым, как в этом примере, используя свойство class:

protocol ResumeBtnSelectorDelegate: class {
    func didPressResumeBtn(resumeBtn:SKSpriteNode)
}

weak var resumeBtnDelegate:ResumeBtnSelectorDelegate?

ARC выполнить всю необходимую нам работу, но если вы считаете, что забыли написать правильное свойство (initiliazation, blocks..), я использовал также некоторые функции, такие как для фаз отладки:

func cleanScene() {
    if let s = self.view?.scene {
        NotificationCenter.default.removeObserver(self)
        self.children
            .forEach {
                $0.removeAllActions()
                $0.removeAllChildren()
                $0.removeFromParent()
        }
        s.removeAllActions()
        s.removeAllChildren()
        s.removeFromParent()
    }
}

override func willMove(from view: SKView) {
    cleanScene()
    self.removeAllActions()
    self.removeAllChildren()
}

Ответ 5

У меня была аналогичная проблема, как вы @user2857148. Я бы представил VC с:

[self presentViewController:myViewController animated:YES completion:nil];

В @implementation myViewController у меня было:

- (void)viewDidLayoutSubviews
{
    // Configure the view.
    SKView * skView = (SKView *)self.view;
    skView.showsFPS = YES;
    skView.showsNodeCount = YES;
    self.ballonMGScene = [[MBDBallonMiniGame alloc] initWithSize:skView.bounds.size andBallonImageNames:self.ballonObjectsArray];
    self.ballonMGScene.parentVC = self;
    self.ballonMGScene.scaleMode = SKSceneScaleModeAspectFill;
    self.ballonMGScene.physicsWorld.gravity = CGVectorMake(0, 0);
    // Present the scene.
    [skView presentScene:self.ballonMGScene];
} 

Проблема была в:

self.ballonMGScene.parentVC = self;

поскольку в:

@interface MBDBallonMiniGame : SKScene <SKPhysicsContactDelegate>

parentVC объявлен с сильным:

@property (nonatomic,strong) WBMMiniGameVCTemplate *parentVC;

Решение 1:

и сменив его на:

@property (nonatomic,weak) WBMMiniGameVCTemplate *parentVC;

решил проблему для меня.

Объяснение: Ссылка на родительский VC (myViewController), который был UIViewController, был где-то сохранен. Поскольку этот VC имел сильную ссылку на SKScene, он был сохранен вместе с ним. У меня даже был консольный вывод из этой SKScene, как будто он все еще активен. Моя лучшая проблема в том, почему это случилось со мной, было то, что у меня были самые сильные указатели.

Решение 2:

В моем myViewController в разделе:

- (void)viewDidDisappear:(BOOL)animated

Я позвонил:

self.ballonMGScene.parentVC = nil;

При выходе из текущего VC (myViewController) я устанавливаю указатель на nil, удаляя память и все вместе с ней.

Эти 2 решения работали для меня. Я тестировал его с помощью отладчика. Потребление памяти пошло вверх и вниз правильно.

Надеюсь, это поможет понять проблему и решения.

Ответ 6

Попробуйте сохранить сцену перед удалением сцены.

-(void)dealloc
{
[sceneView presentScene:nil];
[sceneView release];
[super dealloc];
}

Ответ 7

Сделали несколько шагов, но я полностью решил свою проблему:

1) В My ViewControl я создал метод принудительного уничтожения всех детей:

-(void)destroyAllSub:(SKNode*)node
{
    if(node == nil) return;
    if(![node isKindOfClass:[SKNode class]]) return;

    [node removeAllActions];
    for (SKNode *subNode in node.children) {
        [self destroyAllSub:subNode];
    }
    [node removeAllChildren];
}

2) Так как я создал сильный протокол в своей Сцене и ссылался на него в моем ViewControl, и моя сцена также была сильной, я уничтожил все ссылки следующим образом:

[self.mainScene.view presentScene:nil]; //mainScene: the name of the Scene pointer
self.mainScene.myProt = nil; //myProt: The name of the strong protocol

@autoreleasepool {
    [self destroyAllSub:self.mainScene];
    self.mainScene = nil;
}