Я имею дело с особенно неприятной ошибкой, которая приводит к тому, что основной поток достигает% 100, а пользовательский интерфейс полностью зависает.
По сути, это «симуляция», в которой 100 экземпляров узлов создаются как «ячейки», а 2 других узла - как атакующий и защитник.Ячейки имеют два свойства, зависимость и раздражение, которые изменяются атакующим и защитником каждую секунду.
Я запускаю таймер, который вызывает метод fire () для каждого из нападавших и защитника, пропуская случайно выбранную ячейкув качестве аргумента.
Метод fire () в атакующем будет рассчитывать свою собственную мощность на основе времени T, которое является временем, прошедшим с момента запуска симуляции, и вызывать методы IncreaseAddiction () и IncreaseAnnoyance () для переданных ячеек,прохождение это рассчитанная мощность.То же самое произойдет с методом fire () ячейки защитника, но на этот раз вызываются методы decaeAddiction () и lowerAnnoyance ().
Расчет мощности не является линейной функцией и отличается для атакующего иЗащитник создает моменты доминирования, для атакующего или защитника.Узлы ячеек окрашены в соответствии с их уровнями зависимости и раздражения, что делает их интересной анимацией
Проблема появляется через некоторое время T после начала симуляции.Мне удалось выделить строку «self.annoyanceLevel = newLevel» в качестве виновника, когда удаленный код не показывает это поведение.Кроме того, проблема не возникает при удалении методов fire () у атакующего или защитника, что привело меня к подозрению о состоянии гонки, поэтому я пытался вызывать их по очереди, а не вызывать их одновременно, но это не помогло.Я пытался создать новую очередь отправки и вызывать их оттуда, но это не помогло.Я пытался создать механизм блокировки / разблокировки, помечая ячейку как «inUse» до тех пор, пока setAnnoyance () не вернется, но это не помогло, я попытался поместить изменения в очередь, добавив их в массив и вызвав функцию setAnnoyanceLevels () для отдельного использованиязначения в массиве que но это не помогло.Я не смог найти ничего подобного делению на ноль или другим логическим проблемам, таким как случайные бесконечные циклы.
Я пытался жестко закодировать значение, которое вызывает проблему (по-видимому, оно зависает при изменении с 63 на 100), и это решило проблему, однако, это не помогает мне, так как мне не нужно жестко закодированное число,
Вот упрощенные версии функций
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(fireAtCells), userInfo: nil, repeats: true)
/* The methods that run the simulation */
@objc func fireAtCells(){
for bot in GameEngine().data.attackBots{
var targetCells = GameEngine().data.cells.filter { cell in
return cell.addictionLevel < 100
}
let pickedCell = Int.random(in: 0 ... targetCells.count - 1)
bot.fire(cell: targetCells[pickedCell])
}
}
for bot in GameEngine().data.defenceBots{
var targetCells = GameEngine().data.cells.filter { cell in
return cell.addictionLevel > 0 || cell.annoyanceLevel > 0
}
let pickedCell = Int.random(in: 0 ... targetCells.count - 1)
bot.fire(cell: targetCells[pickedCell])
}
}
}
/* These are the methods on the attacker and defender that calculate the power etc. and increaseAddicton or decreaseAnnoyance, decreaseAddiction methods on the Cells */
func fire(cell: Cell){
let gameStatus = GameEngine()
let pickBullet = Int.random(in: 0 ... bullets.count - 1)
let bullet = bullets[pickBullet]
switch bullet {
case .addict:
cell.decreaseAnnoyance(50)
cell.increaseAddiction(Int(gameStatus.status.attackPower))
bulletColor = .red
case .annoy:
let power = Int(gameStatus.status.attackPower)
cell.increaseAnnoyance(power)
}
}
/* The methods in the Cell objects that adjust it's own properties when called from the attacker or defender, addiction methods are pretty much the same, so ommiting them */
func increaseAnnoyance(_ power: Int){
self.setAnnoyanceLevel(power + self.annoyanceLevel)
}
func decreaseAnnoyance(_ power: Int){
self.setAnnoyanceLevel(self.annoyanceLevel - power)
}
func setAnnoyanceLevel(_ level : Int){
let newLevel = (level > 100) ? 100 : (level < 1 ? 1 : level)
self.annoyanceLevel = newLevel
self.wiggle(interval: 0.5 - 0.004 * Double(level), strength: 20)
self.colorize(color: UIColor.yellow, level: level)
}
Обновление: Мне удалось перехватить выполнение незадолго до того, как процессор достигнет% 100, и похоже, что таймер плохо себя ведет.После того, как incrementAnoyance () возвращается и таймер готовится к запуску метода FireAtCells (), он каким-то образом застревает в бесконечном цикле.В последнем цикле перед выпуском% 100 я вижу, что объект Timer фактически никогда не завершает инициализацию и не вызывает метод ForeAtCells, вместо этого он застревает.
Обновление 2 Хорошо, страннодостаточно, таймер никогда не завершает выполнение в последний раз, но кажется, что ЦП% 100 вызван таймером, используемым некоторыми SKAction на узлах Spritekit.Действия SpriteKit никогда не обращаются к этим свойствам, я не понимаю, почему проблема возникает только тогда, когда свойство объекта изменяется программно.