34

我最近一直在使用 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

4

4 回答 4

55

通过将值传递给初始化程序Int64来转换为将始终在 64 位机器上工作,如果整数超出范围,它将在 32 位机器上崩溃。IntInt64IntInt32.min ... Int32.max

为了安全起见,使用init(truncatingIfNeeded:)初始化器(以前称为init(truncatingBitPattern:)早期 Swift 版本)来转换值:

return Int(truncatingIfNeeded: rowid)

在 64 位机器上,truncatingIfNeeded什么都不做;你只会得到一个(无论如何Int大小都一样)。Int64

在 32 位机器上,这将丢弃前 32 位,但如果它们都是零,那么您并没有丢失任何数据。因此,只要您的值适合 32-bit Int,您就可以在不丢失数据的情况下执行此操作。如果您的值超出范围Int32.min ... Int32.max,这会将值更改为Int64适合 32-bit的值Int,但不会崩溃。


您可以在 Playground 中看到它是如何工作的。由于Int在 Playground 中是 64-bit Int,您可以显式使用 anInt32来模拟 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
于 2015-09-26T04:32:33.403 回答
3

如果您确信该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可能是一个错误。

于 2018-09-13T04:48:55.223 回答
2

这是一个痛苦的屁股。它比 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 中测试

于 2019-08-02T18:31:44.933 回答
-1

事实上,我也一直在使用这个框架,基本上我只是使用了相反的解决方案。每当您看到类型不匹配时

Int64(yourInt) 

(使用 Xcode 7、Swift 2.0 测试)

于 2016-02-23T03:42:53.087 回答