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

Сбой при выдаче результата arc4random() в Int

Я написал простой класс Bag. Сумка заполнена фиксированным соотношением переходов температуры. Это позволяет вам захватывать один случайным образом и автоматически заполнять себя при пустом. Это выглядит так:

class Bag {
    var items = Temperature[]()

    init () {
        refill()
    }

    func grab()-> Temperature {
        if items.isEmpty {
            refill()
        }

        var i = Int(arc4random()) % items.count
        return items.removeAtIndex(i)
    }

    func refill() {
        items.append(.Normal)

        items.append(.Hot)
        items.append(.Hot)

        items.append(.Cold)
        items.append(.Cold)
    }
}

Переменная температуры выглядит следующим образом:

enum Temperature: Int {
    case Normal, Hot, Cold
}

My GameScene:SKScene имеет свойство константного экземпляра bag:Bag. (Я тоже пытался с переменной.) Когда мне нужна новая температура, я вызываю bag.grab(), один раз в didMoveToView и, когда это необходимо, в touchesEnded.

Случайно этот вызов вылетает на строку if items.isEmpty в bag.grab(). Ошибка EXC_BAD_INSTRUCTION. При проверке отладки отображаются элементы size=1 и [0] = (AppName.Temperature) <invalid> (0x10).

Изменить Похоже, я не понимаю информацию отладчика. Даже допустимые массивы показывают size=1 и несвязанные значения для [0] =. Так что никакой помощи нет.

Я не могу заставить его разбиться на игровой площадке. Это, наверное, что-то очевидное, но я в тупике.

4b9b3361

Ответ 1

Функция arc4random возвращает UInt32. Если вы получите значение выше Int.max, приведение Int(...) приведет к сбою.

Использование

Int(arc4random_uniform(UInt32(items.count)))

должно быть лучшим решением.

(Убейте странные сообщения о сбоях в версии Alpha...)

Ответ 2

Я обнаружил, что лучший способ решить это - использовать rand() вместо arc4random()

код в вашем случае может быть:

var i = Int(rand()) % items.count

Ответ 3

Этот метод генерирует случайное значение Int между заданным минимальным и максимальным

func randomInt(min: Int, max:Int) -> Int {
    return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}

Авария, с которой вы столкнулись, связана с тем, что Swift обнаружил несогласованность типа во время выполнения. Поскольку Int!= UInt32 вам придется сначала набирать входной аргумент arc4random_uniform, прежде чем вы сможете вычислить случайное число.

Ответ 4

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

let x = 32
let y = UInt8(x)

Почему? Потому что 32 является возможным значением для int типа UInt8. Но следующий код не сработает:

let x = 332
let y = UInt8(x)

Это потому, что вы не можете назначить 332 неподписанному 8-битовому типу int, он может принимать значения от 0 до 255 и ничего больше.

Когда вы выполняете кавычки на C, int просто усекается, что может быть неожиданным или нежелательным, поскольку программист может не знать, что усечение может иметь место. Поэтому Swift здесь немного отличается. Он позволит такие виды приведения, если не происходит усечения, но если есть усечение, вы получаете исключение во время выполнения. Если вы считаете, что усечение в порядке, то вы должны сделать усечение самостоятельно, чтобы Свифт знал, что это предназначенное поведение, иначе Свифт должен предположить, что это случайное поведение.

Это даже документировано (документация UnsignedInteger):

Преобразование из широкого типа беззнакового целого типа Swift, захват при переполнении.

И то, что вы видите, это "перехват переполнения", что плохо сделано, поскольку, конечно, можно было заставить эту ловушку фактически объяснить, что происходит.

Предполагая, что items никогда не имеет более 2 ^ 32 элементов (бит более 4 миллиардов), следующий код безопасен:

var i = Int(arc4random() % UInt32(items.count))

Если у него может быть более 2 ^ 32 элементов, вы все равно получаете другую проблему, так как тогда вам нужна другая функция случайных чисел, которая производит случайные числа за пределами 2 ^ 32.

Ответ 5

Это автоматически создаст случайный Int для вас:

var i = random() % items.count

i имеет тип Int, поэтому преобразование не требуется!

Ответ 6

Вы можете использовать

Int(rand())

Чтобы предотвратить такие же случайные числа при запуске приложения, вы можете вызвать srand()

srand(UInt32(NSDate().timeIntervalSinceReferenceDate))
let randomNumber: Int = Int(rand()) % items.count

Ответ 7

Этот сбой возможен только на 32-битных системах. Int изменяется между 32-битными (Int32) и 64-битными (Int64) в зависимости от архитектуры устройства (см. документы).

UInt32 max 2^32 − 1. Int64 max 2^63 − 1, поэтому Int64 может легко обрабатывать UInt32.max. Однако Int32 max 2^31 − 1, что означает, что UInt32 может обрабатывать числа, превышающие Int32, и попытка создать Int32 из числа, большего чем 2^31-1, создаст переполнение.

Я подтвердил это, пытаясь скомпилировать строку Int(UInt32.max). На симуляторах и более новых устройствах это просто отлично. Но я подключил свой старый iPod Touch (32-битное устройство) и получил эту ошибку компилятора:

Целое число переполняется при преобразовании из UInt32 в Int

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