swift if-let 不解包可选的 NSDictionary

swift if-let doesn't unwrap optional NSDictionary

中,建议将 [AnyObject] 类型转换为类型数组,但在我的例子中,return 值是一个单数 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")
outputs:
    before intOrThrow(100) and optional unwrap
    before intOrThrow(0) and optional unwrap
    count is 1. x is Optional(["ZERO"])
    before intOrThrow(-100) and optional unwrap
    before intOrThrow(1111) and optional unwrap
    before intOrThrow(200) and block
    count is nil. x is nil
    before intOrThrow(0) and block
    count is Optional(1). x is Optional(["ZERO"])
    before intOrThrow(-200) and block
    before intOrThrow(2222) and block
    count is nil. x is nil
    done intOrThrow
    before strOrNil(3333) and block
    done strOrNil

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

让我们从try?开始。 try? 和普通的旧 try 之间的区别在于 try? 要么成功要么 return niltry 承诺它会成功或抛出一些错误。所做的是改变 return 类型,在这种情况下 JSONObjectWithData 从 returning 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 和语句 returns。在 guard 语句和 if let 语句中,您可以解包一个变量,如果它通过,则在解包链中使用它,就像我的 guard 语句首先解包 data 然后 jsonResult 使用包装数据,依此类推。