【问题标题】:swift if-let doesn't unwrap optional NSDictionaryswift if-let 不会打开可选的 NSDictionary
【发布时间】:2015-10-31 23:14:22
【问题描述】:

this stackoverflow question 中,建议将[AnyObject] 类型转换为类型化数组,但在我的情况下,返回值是单数AnyObject 向下转换为单数JSONObjectWithData

// ObjC def: public class func JSONObjectWithData(data: NSData, options opt: NSJSONReadingOptions) throws -> AnyObject
if let jsonResult = try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary {
     if let results = jsonResult!["results"] as? NSArray { // ! needed or compile error
     }
}

如何让 Swift 自动解包jsonResult

更新:这是一个更好的例子来说明问题:

func intOrThrow(arg: Int) throws -> AnyObject? {
    if arg < 0 {
        throw NSError(domain: "test", code: 400, userInfo: nil)
    } else if arg == 0 {
        return ["ZERO"]
    } else if arg > 1000 {
        return nil
    }
    return arg * 2
}

func strOrNil(arg: Int) -> String? {
    if arg < 0 ||  arg > 1000 {
        return nil
    }
    return "NUMBER\(arg)"
}

print("before intOrThrow(100) and optional unwrap")
if let x = try? self.intOrThrow(100) as? [String], // incorrect type
results = x?.count {
    print("count is \(results). x is \(x)")
}
print("before intOrThrow(0) and optional unwrap")
if let x = try? self.intOrThrow(0) as? [String], // good type
results = x?.count {
    print("count is \(results). x is \(x)")
}
print("before intOrThrow(-100) and optional unwrap")
if let x = try? self.intOrThrow(-100) as? [String], // throw
results = x?.count {
    print("count is \(results). x is \(x)")
}
print("before intOrThrow(1111) and optional unwrap")
if let x = try? self.intOrThrow(1111) as? [String], // nil
results = x?.count {
    print("count is \(results). x is \(x)")
}

print("before intOrThrow(200) and block")
if let x = try? self.intOrThrow(200) as? [String] { // incorrect type
    print("count is \(x?.count). x is \(x)") // still require ! or ?, else compile error
}
print("before intOrThrow(0) and block")
if let x = try? self.intOrThrow(0) as? [String] { // good type
    print("count is \(x?.count). x is \(x)") // still require ! or ?, else compile error
}
print("before intOrThrow(-200) and block")
if let x = try? self.intOrThrow(-200) as? [String] { // throw
    print("count is \(x!.count). x is \(x)") // still require ! or ?, else compile error
}
print("before intOrThrow(2222) and block")
if let x = try? self.intOrThrow(2222) as? [String] { // nil
    print("count is \(x?.count). x is \(x)") // still require ! or ?, else compile error
}
print("done intOrThrow")

print("before strOrNil(3333) and block")
if let x = self.strOrNil(2222) { // nil, no type cast
    print("count is \(x.lowercaseString). x is \(x)") // don't require ! or ?
}
print("done strOrNil")
输出: 在 intOrThrow(100) 和可选展开之前 在 intOrThrow(0) 和可选展开之前 计数为 1。x 是可选的(["ZERO"]) 在 intOrThrow(-100) 和可选展开之前 在 intOrThrow(1111) 和可选展开之前 在 intOrThrow(200) 之前并阻止 计数为零。 x 为零 在 intOrThrow(0) 之前并阻塞 计数是可选的(1)。 x 是可选的([“零”]) 在 intOrThrow(-200) 之前并阻止 在 intOrThrow(2222) 之前并阻止 计数为零。 x 为零 完成 inOrThrow 在 strOrNil(3333) 和块之前 完成 strOrNil

【问题讨论】:

    标签: ios swift optional unwrap


    【解决方案1】:

    所以这里发生了一些我会改变的事情,我会仔细解释为什么我个人会做不同的事情。

    让我们从try? 开始。 try? 和普通的 try 之间的区别在于 try? 要么成功要么返回 niltry 承诺它会成功或抛出一些错误。这样做是改变返回类型,在这种情况下 JSONObjectWithData 从返回 AnyObject 变为 AnyObject?

    do {
        let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, .MutableContainers)
    } catch {} //catch and report on the error
    

    这样,如果没有抛出错误,jsonResult 可以保证以AnyObject 的形式存在。然后你可以做if let jsonResult = jsonResult["results"] as? NSArray。 顺便说一句,我不会使用NSArray,如果你能帮忙的话,尽量坚持使用 Swift 原生类型(as? [AnyObject] 之类的就可以了)。

    现在您的代码如下所示:

    var data: NSData! = NSData(base64EncodedString: "SGVsbG8gd29ybGQh", options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
    do {
    let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
        if let results = jsonResult["results"] as? [AnyObject] {
            print(results)
        } else {
            print("how did this happen?")
        }
    } catch {
    
    }
    

    注意:您也不需要向下转换为 [AnyObject] 来编译它,但 results 将是 AnyObject

    现在在这种情况下,我不是do catch 的忠实粉丝。处理这个 imo 的更好方法是:

    guard let data = NSData(base64EncodedString: "SGVsbG8gd29ybGQh", options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters),
        jsonResult = try? NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers),
        result = jsonResult["results"] as? [AnyObject] else {
        return 
    }
    

    现在它是一个语句,编译得很好,所有三个选项都在一个语句中展开。如果guard 语句通过,datajsonResultresults 将全部被填充。如果有任何失败,则将调用 else 并返回语句。在guard 语句和if let 语句中,您可以解包一个变量,如果它通过,则在解包链中使用它,就像我的guard 语句首先解包data 然后jsonResult 使用包装数据,等等。

    【讨论】:

    • 感谢您的回答。虽然我不好奇为什么if let 不能正常工作。你是对的,它与try? 有关。不过,对我来说,它看起来像是一个编译器(或语言定义)错误,因为如果 try? 产生 nil,则应该跳过 if let 块作为非尝试用法。
    • 错字:“我大多是古玩……”
    • 为什么你的原始代码没有编译?你的意思是没有as? NSArray 演员表?
    • 不,它只抱怨jsonResult 必须用~? 解包。
    猜你喜欢
    • 2015-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    • 2015-02-09
    • 1970-01-01
    • 2021-05-02
    • 2023-03-24
    相关资源
    最近更新 更多