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 nil
而 try
承诺它会成功或抛出一些错误。所做的是改变 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
语句通过,data
、jsonResult
和 results
都将被填充。如果有任何失败,将调用 else 和语句 returns。在 guard
语句和 if let
语句中,您可以解包一个变量,如果它通过,则在解包链中使用它,就像我的 guard
语句首先解包 data
然后 jsonResult
使用包装数据,依此类推。
在 [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 nil
而 try
承诺它会成功或抛出一些错误。所做的是改变 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
语句通过,data
、jsonResult
和 results
都将被填充。如果有任何失败,将调用 else 和语句 returns。在 guard
语句和 if let
语句中,您可以解包一个变量,如果它通过,则在解包链中使用它,就像我的 guard
语句首先解包 data
然后 jsonResult
使用包装数据,依此类推。