Могу ли я перенести Int64 прямо в Int?

В последнее время я использую SQLite.swift для создания моей базы данных приложений. И я определяю все мои столбцы INTEGER с типом Int64, как объясняет документация.

Но время от времени мне нужно, чтобы Int64 был просто Int. Поэтому мой вопрос: если я это сделаю:

//Create a table with Int instead of Int64
let test_id = Expression<Int>("test_id")
let tests = db["tests"]

db.create(table: tests, ifNotExists: true){ t in
    t.column(test_id)
}


class func insertTest(t: Int) -> Int{
    //insert.rowid returns an Int64 type
    let insert = tests.insert(test_id <- t)
    if let rowid = insert.rowid{
        //directly cast Int64 into Int
        return Int(rowid)
    }
    return 0
}

Будет ли это правильно?

Конечно, я его протестировал. И он работает, но я читал qaru.site/info/357450/...

И похоже, что у меня может быть проблема с 32-битными устройствами...

Если это не так, как я могу сделать Int64 в Int?

Ответы

Ответ 1

Преобразование Int64 в Int путем передачи значения Int64 в инициализатор Int всегда будет работать на 64-разрядной машине и завершится сбоем на 32-разрядной машине, если целое число находится вне диапазона Int32.min... Int32.max.

В целях безопасности используйте init(truncatingIfNeeded:) (ранее известный как init(truncatingBitPattern:) в более ранних версиях Swift) для преобразования значения:

return Int(truncatingIfNeeded: rowid)

На 64-битной машине truncatingIfNeeded ничего не будет делать; вы просто получите Int (который в любом случае имеет тот же размер, что и Int64).

На 32-битной машине это отбросит старшие 32 бита, но если все они равны нулю, то вы не потеряете данные. Таким образом, пока ваше значение будет соответствовать 32-битному Int, вы можете делать это без потери данных. Если ваше значение находится за пределами диапазона Int32.min... Int32.max, это изменит значение Int64 на то, что соответствует 32-разрядному типу Int, но оно не будет аварийно завершать работу.


Вы можете увидеть, как это работает на детской площадке. Поскольку Int на игровой площадке представляет собой 64-битное Int, вы можете явно использовать Int32 для имитации поведения 32-битной системы.

let i: Int64 = 12345678901  // value bigger than maximum 32-bit Int

let j = Int32(truncatingIfNeeded: i)  // j = -539,222,987
let k = Int32(i)                        // crash!

Обновление для Swift 3/4

В дополнение к init(truncatingIfNeeded:) который все еще работает, Swift 3 вводит сбойные инициализаторы для безопасного преобразования одного целочисленного типа в другой. Используя init?(exactly:) вы можете передать один тип для инициализации другого, и он возвращает nil если инициализация не удалась. Возвращаемое значение является необязательным, которое должно быть развернуто обычными способами.

Например:

let i: Int64 = 12345678901

if let j = Int32(exactly: i) {
    print("\(j) fits into an Int32")
} else {
    // the initialization returned nil
    print("\(i) is too large for Int32")
}

Это позволяет вам применять оператор объединения nil для предоставления значения по умолчанию, если преобразование завершается неудачно:

// return 0 if rowid is too big to fit into an Int on this device
return Int(exactly: rowid) ?? 0

Ответ 2

Если вы уверены, что значение Int64 может быть представлено точно как Int, используйте Int(truncatingIfNeeded:), например:

let salary: Int64 = 100000
let converted = Int(truncatingIfNeeded: salary)

Для сборок, предназначенных для 32-битных устройств, диапазон для Int ограничен -2147483648 до 2147483647, так же, как Int32. Значения за пределами этого диапазона будут спокойно отбрасываться из старших битов. Это приводит к мусору, часто противоположного знака.

Если значение может быть вне диапазона, и вы хотите обработать это условие, используйте Int(exactly:), например:

if let converted = Int(exactly: salary) {
    // in range
    ... converted ...
} else {
    // out-of-range
    ...
}

В конкретном случае rowids использование Int64 вместо Int было преднамеренным выбором дизайна API, и усечение до Int могло бы быть ошибкой.

Ответ 3

На самом деле я тоже работал с этой картой, и в основном я просто использовал противоположное решение. Всякий раз, когда вы видите, что типы не совпадают, просто выполните

Int64(yourInt) 

(проверено с помощью Xcode 7, Swift 2.0)