web-dev-qa-db-ja.com

Int64をIntに直接キャストできますか?

最近、SQLite.Swiftを使用してアプリデータベースを構築しています。そして、ドキュメントで説明されているように、すべてのINTEGER列をInt64型で定義しています。

しかし、たまにInt64Intである必要があります。だから私の質問は、私がこれを行う場合です:

//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ビットデバイスで問題を抱えているようです...

これが間違っている場合、どのようにしてInt64Intにキャストできますか?

23
Renato Parreira

Int64値をInt初期化子に渡すことでInt64Intに変換すると、常に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
33
vacawama

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への切り捨てはバグになる可能性があります。

2
jedwidz

実際、私もこのフレームワークを使用してきましたが、基本的には反対のソリューションを使用しました。タイプが一致しないことがわかったときはいつでも

Int64(yourInt) 

(Xcode 7でテスト済みSwift 2.0)

0
René Blaser

それはお尻の痛みです。それは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

0
Alex Zavatone