【问题标题】:Type checks on Int and Bool values are returning incorrectly in Swift 4.2Int 和 Bool 值的类型检查在 Swift 4.2 中返回不正确
【发布时间】:2023-04-08 06:23:01
【问题描述】:

我目前有一个关于 Swift 4.2 的问题,我想请求人们帮助解决。

目前,我正在反序列化一个 JSON 响应,它是一个字典,其键的类型为 String,其值的类型可以为 IntBool。一个很好的例子如下:

{
    "number_of_likes": 0,
    "is_liked": true

}

当我反序列化对象时,JSON 响应的类型为 [String: Any],这是预期的。

任务: 我需要创建一个数组,详细说明哪些键的类型为 Bool 并设置为 true

问题:

使用上面突出显示的响应运行以下代码时:

guard let json = json as? [String: Any] else {
    return
}

for key in dict.keys {

    print("KEY: \(key)")

    let value = dict[key]

    if value is Int {
        print("It is an integer")
    }

    if value is Bool {
        print("It is a bool")
    }

}

返回字符串

控制台打印如下:

???? t+20.765 KEY: number_of_likes
???? t+20.765 It is an integer
???? t+20.765 It is a bool

???? t+20.765 KEY: is_liked
???? t+20.765 It is an integer
???? t+20.765 It is a bool

如您所见,0 Int 值导致控制台打印它是 Bool 和 Int 类型,而 true Bool 值导致要打印的控制台是 IntBool 类型。

对于上下文,我确实在 StackOverflow 上找到了以下问题,但除非我检查类型 CFBool 而不是类型 Bool,否则答案不起作用。

Is it possible to distinguish Bool and Int in Swift?

如果我做错了什么,或者这是 Swift 4.2 的问题,有人可以告诉我吗?

非常感谢您的帮助,因此感谢所有阅读本文的人。

【问题讨论】:

  • 您显示的代码不足以解释您声称的输出。请显示真实完整的代码。从实际的 JSON 字符串开始,展示如何从中获取 json。您应该使用 Decodable,但似乎没有。
  • 除了你的输出之外,你不应该提前知道一个键是 bool 还是 Int 并相应地对其进行硬编码??
  • 什么是dict?使用结构而不是字典

标签: swift


【解决方案1】:

遗憾的是,如果不使用核心基础类型或 Objective-C @encode 字符串,您将无法解决这个问题。

问题在于,在幕后,Foundation 的 JSON 序列化使用 NSNumber 来包装整数和布尔值。所以 JSON 0 和 JSON true 都转换为 NSNumber 对象,Swift 愿意根据请求将这些 NSNumber 对象转换为 IntBool

然而,JSON 布尔值实际上被转换为 NSNumber 的子类,称为 __NSCFBoolean,这是 CFBooleanRef(在 Swift 中为 CFBoolean)引用的类型:

import Foundation

let json = """
{
    "number_of_likes": 0,
    "is_liked": true

}
"""

let data = json.data(using: .utf8)!
let jso = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]

for key in jso.keys {
    if let value = jso[key] as? NSNumber {
        print("\(key) \(type(of: value)) \(String(cString: value.objCType))")
    }
}

输出:

number_of_likes __NSCFNumber q
is_liked __NSCFBoolean c

没有记录说 Foundation JSON 序列化将 JSON 布尔值解码为 Core Foundation CFBooleans,但它不太可能改变。

所以这里有一个 Core Foundation 的测试方法:

if let isLikedCF = jso["is_liked"] as CFTypeRef?,
    CFGetTypeID(isLikedCF) == CFBooleanGetTypeID()
{
    print("it's bool")
} else {
    print("it's not bool")
}

我们在这里所做的是将 JSON 字典中的值转换为 CFTypeRef(这是对任何 Core Foundation 类型的引用,Foundation 的 JSONSerialization 返回的所有内容都免费桥接到 Core Foundation type),然后检查Core Foundation对象的类型ID是否为CFBoolean类型ID。

使用 Core Foundation 进行测试的另一种方法是识别只有两个 CFBooleanRef 值,kCFBooleanTruekCFBooleanFalse。您可以使用=== 查看jso["is_liked"] as? NSNumber 是否与这两个值之一相同:

if let isLikedNumber = jso["is_liked"] as? NSNumber,
    isLikedNumber === kCFBooleanTrue || isLikedNumber === kCFBooleanFalse
{
    print("it's bool")
} else {
    print("it's not bool")
}

您还可以通过检查NSNumber 的Objective-C 类型代码进行测试。你投射jso["is_liked"] as? NSNumber,询问它的objCType,将生成的C字符串转换为Swift String,并将其与"c"进行比较。如果是这样,它是一个布尔值。否则,它不是。

if let isLikedNumber = jso["is_liked"] as? NSNumber {
    if String(cString: isLikedNumber.objCType) == "c" {
        print("it's bool")
    } else {
        print("it's not bool")
    }
}

c 来自 @encode(BOOL)(在 Objective-C 中),其中 BOOLsigned char 的 typedef。这真是个晦涩难懂的东西。我建议使用 Core Foundation 测试(如上所示),因为它更易于理解且文档记录更好。

【讨论】:

  • 很棒的详细解释。谢谢
【解决方案2】:

你应该这样做

import UIKit

var str = """
{
"number_of_likes": 0,
"is_liked": true
}
"""
struct JsonStruct: Decodable {
    var boolWithKey: [String: Bool]?
    var intWithKey: [String: Int]?
    init(from decoder: Decoder) {
        guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
            fatalError()
        }
        for key in container.allKeys {
            if let possibleInt = try? container.decode(Int.self, forKey: key) {
                intWithKey = [key.stringValue: possibleInt]
            }
            if let possibleBool = try? container.decode(Bool.self, forKey: key) {
                boolWithKey = [key.stringValue: possibleBool]
            }
        }
        print(container.allKeys)
    }

    struct CodingKeys: CodingKey {
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        var intValue: Int?
        init?(intValue: Int) {
            return nil
        }
    }
}
let jsonData = str.data(using: .utf8)!
let jsonDecoder = JSONDecoder()
let jsonStruct = try! jsonDecoder.decode(JsonStruct.self, from: jsonData)
print("\(jsonStruct.boolWithKey)")
print("\(jsonStruct.intWithKey)")

输出:

CodingKeys(stringValue: "number_of_likes", intValue: nil),

CodingKeys(stringValue: "is_liked", intValue: nil)]

可选(["is_liked": true])

可选(["number_of_likes": 0])

【讨论】:

  • 他不需要那样做。他的类型实际上并不是动态的。他错误地描述了这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
  • 2016-03-02
  • 1970-01-01
  • 1970-01-01
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多