Невозможно обнаружить утечку памяти с помощью инструментов XCode - PullRequest
0 голосов
/ 27 января 2019

Проблема

Ранее я обнаружил, что использование памяти в моей игре увеличивалось только при перемещении тайлов, но никогда больше не уменьшалось.Исходя из этого, я мог сказать, что произошла утечка памяти.

Затем я начал использовать инструменты XCode, к которым я очень новичок.Итак, я следовал многим вещам из этой статьи , особенно Recording Options, а затем я установил режим, чтобы показать Call Tree.

Результаты инструментов

Instruments - Results of testing for leaks

В чем мне нужна помощь?

У меня есть две функции, которые просто перемещают все плитки вдоль этой строки / столбца, а затем клонируют плитку вконец (с использованием node.copy()), чтобы все могло «зацикливаться», отсюда и название проекта.

Мне кажется, что клонирование плитки может вызывать некоторый цикл хранения, однако оно сохраняется в переменной внутри функцииобъем.После запуска SKAction на клоне я удаляю плитку со сцены, используя copiedNode.removeFromParent().

Так что может быть причиной этой утечки памяти?Могу ли я искать не в том месте?


Код

Я сократил этот код до того, что считаю необходимым.

Объявление в верхней части класса:

/// Delegate to the game scene to reference properties.
weak var delegate: GameScene!
/// All the cloned tiles currently on the board.
private var cloneTiles = [SKSpriteNode]()

Клонирование плитки в движущихся плитках выполняет следующие функции:

/// A duplicate of the current tile.
let copiedNode = currentTile.node.copy() as! SKSpriteNode // Create copy
cloneTiles.append(copiedNode) // Add as a clone
delegate.addChild(copiedNode) // Add to the scene
let copiedNodeAction = SKAction.moveBy(x: movementDifference, y: 0, duration: animationDuration) // Create the movement action

// Run the action, and then remove itself
copiedNode.run(copiedNodeAction) {
    self.cloneTiles.remove(at: self.cloneTiles.firstIndex(of: copiedNode)!)
    copiedNode.removeFromParent()
}

Функция немедленного перемещения плиток:

/// Move all tiles to the correct location immediately.
private func moveTilesToLocationImmediately() {
    // Remove all clone tiles
    cloneTiles.forEach { $0.removeFromParent() }
    cloneTiles.removeAll()

    /* Moves tiles here */
}

Есть ли что-то, что мне нужно объявитькак weak var или как?Я знаю, как происходят циклы сохранения, но не понимаю, почему он существует в этом коде, поскольку я удаляю клонированную ссылку на плитку из массива cloneTiles.


Примерно там, где происходит утечка (помогаетMark Szymczyk)

Вот что произошло после того, как я дважды щелкнул по функции перемещения плиток в стеке вызовов (см. Его ответ ниже):

Instruments - Finding the memory leak

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

Пожалуйста, оставьте любые советы или вопросы по этому поводу, чтобы эту проблему можно было решить!

Подробнеерасследование

Я сейчас пытаюсь разобраться с инструментами Xcode, но я все еще очень стараюсь найти эту утечку памяти.Вот панель утечек, которая может помочь:

Instruments - Leaks panel

Instruments - Leak's history

Даже после попытки [weak self] Мне все еще не повезло:

Instruments - Using [weak self] within the closure

Даже история утечек по-прежнему выглядит так же, как [weak self] в закрытии.

Продолжаем пытаться разрешить ссылочный цикл

В настоящее время @matt помогает мне решить эту проблему.Я изменил несколько строк кода, добавив такие вещи, как [unowned self]:

// Determine if the tile will roll over
if direction == .up && movementDifference < 0 || direction == .down && movementDifference > 0 {
    // Calculate where the clone tile should move to
    movementDifference -= rollOverDistance

    /// A duplicate of the current tile.
    let copiedNode = currentTile.node.copy() as! SKSpriteNode // Create copy
    cloneTiles.append(copiedNode) // Add as a clone
    delegate.addChild(copiedNode) // Add to the scene
    let copiedNodeAction = SKAction.moveBy(x: 0, y: movementDifference, duration: animationDuration) // Create the movement action

    // Run the action, and then remove itself
    copiedNode.run(copiedNodeAction) { [unowned self, copiedNode] in
        self.cloneTiles.remove(at: self.cloneTiles.firstIndex(of: copiedNode)!).removeFromParent()
    }

    // Move the original roll over tile back to the other side of the screen
    currentTile.node.position.y += rollOverDistance
}

/// The normal action to perform, moving the tile by a distance.
let normalNodeAction = SKAction.moveBy(x: 0, y: movementDifference, duration: animationDuration) // Create the action
currentTile.node.run(normalNodeAction) { [unowned self] in // Apply the action
    if forRow == 1 { self.animationsCount -= 1 } // Lower animation count for completion
}

К сожалению, я не смог сделать свойство copiedNode weak, поскольку оно всегда было бы мгновенно nilunowned вызвал сбой при чтении ссылки после освобождения.Вот также график Cycles & Roots, если это полезно:

Instruments - Cycles & Roots graph


Спасибо за любую помощь!

Ответы [ 2 ]

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

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

То, что вы хотите сделать, это сделать все, что входит в метод действия weak, чтобы не было сильного захвата методом действия.Затем в методе действия вы хотите немедленно сохранить эти слабые ссылки, чтобы они не исчезли из-под вас.Это называется "слабый сильный танец".Как это:

    copiedNode.run(copiedNodeAction) { [weak self, weak copiedNode] in
        if let `self` = self, let copiedNode = copiedNode {
            // do stuff here
            // be sure to log so you know we arrived here at all, as we might not
        }
    }
0 голосов
/ 28 января 2019

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

Подробнее об инструментах вы можете узнать в следующей статье:

Измерение использования памяти вашего приложения с помощью инструментов

...