我通过api json响应(我将其转换为[String: Any]
字典)收到一组整数。这些整数保证在10 ... -10(包括)范围内。在我的模型中,我想将它们存储为Int8
。
这就是我将我的json字典转换为它的模型对象的方法,但我想知道是否有一种更惯用的方法可以确保json中的整数真正适合Int8
?
func move(with dict: JSONDictionary) -> Move? {
if let rowOffset = dict[JSONKeys.rowOffsetKey] as? Int,
let colOffset = dict[JSONKeys.colOffsetKey] as? Int {
if let rowInt8 = Int8(exactly: rowOffset),
let colInt8 = Int8(exactly: colOffset) {
return Move(rowOffset: rowInt8, colOffset: colInt8)
}
else {
print("Error: values out of range: (row: \(rowOffset), col: \(colOffset)")
}
} else {
print("Error: Missing key, either: \(JSONKeys.rowOffsetKey) or \(JSONKeys.colOffsetKey)")
}
return nil
}
请注意,无论传入的int值是什么,执行以下操作始终会失败:
if let rowOffset = dict[JSONKeys.rowOffsetKey] as? Int8,
let colOffset = dict[JSONKeys.colOffsetKey] as? Int8 {
...
这就是我将传入的json转换为字典的方式。有问题的json是深层嵌套的,包含几种不同的类型。
typealias JSONDictionary = [String: Any]
let jsonDict = try JSONSerialization.jsonObject(with: data) as? JSONDictionary
答案 0 :(得分:8)
以下是将Int
(来自NSNumber
)转换为Int8
的一些选项:
只需使用Int8
初始化程序转换init(_: Int)
如果您的Int
值保证适合Int8
,那么使用Int8(value)
进行转换就可以了。
如果您获得的Int
不适合Int8
,您的程序将会崩溃:
let i = 300
let j = Int8(i) // crash!
使用Int8
初始值设定项init(truncatingIfNeeded: BinaryInteger)
进行初始化
为了更加安全,您应该使用init(truncatingIfNeeded: BinaryInteger)
初始化程序:
let i = 300
let j = Int8(truncatingIfNeeded: i) // j = 44
这确实产生了可能不合适的改变值,但它可以防止崩溃。
明确检查有效值的范围
作为另一种选择,您可以明确检查范围:
if (-10...10).contains(i) {
j = Int8(i)
} else {
// do something with the error case
}
检查的优点是您可以指定有效范围,而不是仅在范围超出适合Int8
的值时检测错误。
使用Int8
初始值设定项init(exactly: Int)
进行初始化
您的代码目前正在使用这种安全初始化Int8
值的方法。这很有用,因为如果值不适合nil
,它将返回Int8
。因此,它可以使用可选绑定进行检查,或者可以与 nil合并运算符 ??
结合使用,以提供默认值(如果您有适当的值) :
// Determine Int8 value, use 0 if value would overflow an Int8
let j = Int8(exactly: i) ?? 0
在Swift 3.0.1及以上
中直接投放NSNumber
as Int8
正如@Hamish和@OOPer在评论中提到的,现在可以将NSNumber
直接投射到Int8
。
let i: NSNumber = 300
let j = i as Int8 // j = 44
这与使用init(truncatingIfNeeded: BinaryInteger)
具有相同的截断效果。
总之,您当前的代码是安全的,并且可能是您问题的最佳解决方案,除非您想要检测值何时超过当前所需的-10...10
范围,在这种情况下,显式检查将是更好的选择。
答案 1 :(得分:2)
如@vacawama
let j = Int8(exactly: i) ?? 0
是将Int转换为UInt8的理想且安全的方法,但有时我需要获取UInt8的最大值和最小值而不是零。我正在使用扩展程序:
extension UInt8 {
/// Safely converts Int to UInt8, truncate remains that do not fit in UInt8.
/// For instance, if Int value is 300, UInt8 will be 255, or if Int value is -100, UInt8 value will be 0
init(truncateToFit int: Int) {
switch int {
case _ where int < UInt8.min: self.init(UInt8.min)
case _ where int > UInt8.max: self.init(UInt8.max)
default: self.init(int)
}
}
/// Safely converts Float to UInt8, truncate remains that do not fit in UInt8.
/// For instance, if Float value is 300.934, UInt8 will be 255, or if Float value is -100.2342, UInt8 value will be 0
init(truncateToFit float: Float) {
switch float {
case _ where float < Float(UInt8.min): self.init(UInt8.min)
case _ where float > Float(UInt8.max): self.init(UInt8.max)
default: self.init(float)
}
}
/// Safely converts Double to UInt8, truncate remains that do not fit in UInt8.
/// For instance, if Double value is 300.934, UInt8 will be 255, or if Double value is -100.2342, UInt8 value will be 0
init(truncateToFit double: Double) {
switch double {
case _ where double < Double(UInt8.min): self.init(UInt8.min)
case _ where double > Double(UInt8.max): self.init(UInt8.max)
default: self.init(double)
}
}
}
例如:
let value = UInt8(truncateToFit: Int.max) // value == 255
更新:
我发现所有数字的标准实现都符合BinaryInteger协议,例如Int,Int8,Int16,Int32等。
let value = UInt8(clamping: 500) // value == 255
let secondValue = UInt8(clamping: -500) // secondValue == 0
但是对于Double和Float,有一个优雅的解决方案,所有BinaryInteger类型的一个扩展
extension BinaryInteger where Self: FixedWidthInteger {
/// Safely converts Float to BinaryInteger (Uint8, Uint16, Int8, and so on), truncate remains that do not fit in the instance of BinaryInteger range value.
/// For instance, if Float value is 300.934, and self is UInt8, it will be 255, or if Float value is -100.2342, self value will be 0
init(truncateToFit float: Float) {
switch float {
case _ where float < Float(Self.min): self.init(Self.min)
case _ where float > Float(Self.max): self.init(Self.max)
default: self.init(float)
}
}
/// Safely converts Double to BinaryInteger (Uint8, Uint16, Int8, and so on), truncate remains that do not fit in the instance of BinaryInteger range value.
/// For instance, if Double value is 300.934, and self is UInt8, it will be 255, or if Float value is -100.2342, self value will be 0
init(truncateToFit double: Double) {
switch double {
case _ where double < Double(Self.min): self.init(Self.min)
case _ where double > Double(Self.max): self.init(Self.max)
default: self.init(double)
}
}
}
例如:
let valueUInt16 = UInt16(truncateToFit: 5000000.0) // valueUInt16 == 65535
let valueInt8 = Int8(truncateToFit: 5000000.0) // valueInt8 == 127
let valueUInt8 = UInt8(truncateToFit: -500.0) // valueUInt8 == 0