最近、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
}
正しいでしょうか?
もちろんテストしました。そしてそれは動作しますが、私は読んでいた Stackoverflowのこの質問
そして、私は32ビットデバイスで問題を抱えているようです...
これが間違っている場合、どのようにしてInt64
をInt
にキャストできますか?
Int64
値をInt
初期化子に渡すことでInt64
をInt
に変換すると、常に64ビットマシンで動作し、32ビットでクラッシュします。整数がInt32.min ... Int32.max
の範囲外の場合、ビットマシン。
安全のために、init(truncatingIfNeeded:)
イニシャライザー(以前のSwiftバージョンではinit(truncatingBitPattern:)
として知られていました)を使用して値を変換します。
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:)
を使用すると、1つの型を渡して別の型を初期化でき、初期化に失敗した場合は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
If _Int64
_値がInt
として正確に表現できると確信している場合は、Int(truncatingIfNeeded:)
を使用します。例:
_let salary: Int64 = 100000
let converted = Int(truncatingIfNeeded: salary)
_
32ビットデバイスを対象とするビルドの場合、Int
の範囲は、_Int32
_と同じ-2147483648〜2147483647に制限されます。その範囲外の値は、静かに上位ビットが破棄されます。これにより、多くの場合、反対の符号のゴミが発生します。
値が範囲外である可能性があり、その条件を処理する場合は、Int(exactly:)
を使用します。例:
_if let converted = Int(exactly: salary) {
// in range
... converted ...
} else {
// out-of-range
...
}
_
ROWIDの特定のケースでは、Int
ではなく_Int64
_を使用することは意図的なAPI設計の選択であり、Int
への切り捨てはバグになる可能性があります。
実際、私もこのフレームワークを使用してきましたが、基本的には反対のソリューションを使用しました。タイプが一致しないことがわかったときはいつでも
Int64(yourInt)
(Xcode 7でテスト済みSwift 2.0)
それはお尻の痛みです。それはObjective-Cよりも途方もなく馬鹿げている。しかし、これがあなたのやり方です。
UInt64
を使用している場合、このようにします。
let thatVarYouWantToConvert: UInt64 = 1
let myInt: Int = Int(exactly:thatVarYouWantToConvert ?? 0)
UInt64?
を使用している場合、このようにします。
let thatVarYouWantToConvert: UInt64? = 1
let myInt: Int = Int(exactly:thatVarYouWantToConvert ?? 0) ?? 0
私が言ったように、「Objective-Cよりも途方もなく馬鹿げている」。
テスト済みSwift 4.2、Xcode 10.2.1