Thinking in Swift 系列文章翻译1
第一篇:名叫拯救小马🐴
这里是原文地址 原文链接
本系列一共4篇,讲oc与swift编程的差异的。
本篇是关于 可选值optionals, 强制展开 forced-unwrapped optionals,非强制展开 guard , if let等的讨论。
1.Objc code
首先看一段OC的代码, 就是解析json,然后转换为model
@interface ListItem : NSObject
@property(strong) UIImage* icon;
@property(strong) NSString* title;
@property(strong) NSURL* url;
@end
@implementation ListItem
+(NSArray*)listItemsFromJSONData:(NSData*)jsonData {
NSArray* itemsDescriptors = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSMutableArray* items = [NSMutableArray new];
for (NSDictionary* itemDesc in itemsDescriptors) {
ListItem* item = [ListItem new];
item.icon = [UIImage imageNamed:itemDesc[@"icon"]];
item.title = itemDesc[@"title"];
item.url = [NSURL URLWithString:itemDesc[@"url"]];
[items addObject:item];
}
return [items copy];
}
@end
2. 从oc直接翻译成swift代码
class ListItem {
var icon: UIImage?
var title: String = ""
var url: NSURL!
static func listItemsFromJSONData(jsonData: NSData?) -> NSArray {
let jsonItems: NSArray = try! NSJSONSerialization.JSONObjectWithData(jsonData!, options: []) as! NSArray
let items: NSMutableArray = NSMutableArray()
for itemDesc in jsonItems {
let item: ListItem = ListItem()
item.icon = UIImage(named: itemDesc["icon"] as! String)
item.title = itemDesc["title"] as! String
item.url = NSURL(string: itemDesc["url"] as! String)!
items.addObject(item)
}
return items.copy() as! NSArray
}
}
3. 分析问题
新手把强制解析用在各个地方。 value! 还有 value as! String 还有 try!等。 当值为nil的时候,会直接crash。
Optionals 可选值是我们的朋友。当我们写oc的时候,我们经常忘记处理这种情况。但是,我们用swift的时候,应该去可选值等处理值为nil的情况。
You should never force-unwrap a value, except when you really know what you’re doing. Keep in mind that every time you add a ! just to please the compiler, you’re killing a pony 🐴.
你不应该使用! 除非你真的知道你在做什么。 记住每当你用一个 ! 去讨好编译器,你在杀死一只小马🐴。
4. 如何处理这种情况
我们应该如何做去避免到处使用 !
- 使用可选绑定 if let x = optional { 使用x }
- 使用 as? 代替 as!, 如果转换失败会返回nil
- 使用 try?代替 try! 如果执行失败返回nil。
所以,代替! 后的代码如下:
class ListItem {
var icon: UIImage?
var title: String = ""
var url: NSURL!
static func listItemsFromJSONData(jsonData: NSData?) -> NSArray {
if let nonNilJsonData = jsonData {
if let jsonItems: NSArray = (try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: [])) as? NSArray {
let items: NSMutableArray = NSMutableArray()
for itemDesc in jsonItems {
let item: ListItem = ListItem()
if let icon = itemDesc["icon"] as? String {
item.icon = UIImage(named: icon)
}
if let title = itemDesc["title"] as? String {
item.title = title
}
if let urlString = itemDesc["url"] as? String {
if let url = NSURL(string: urlString) {
item.url = url
}
}
items.addObject(item)
}
return items.copy() as! NSArray
}
}
return [] // In case something failed above
}
}
5. 大括号金字塔
如果到处使用if let 来解包可选值, 会造成大括号金字塔。就是逻辑嵌套过多。
有一些替代的方式可以解决这种情况
- 一些情况下可以使用 if let x= opt1,y= opt2 来代替两个if let嵌套
- 可以使用guard 来代替 if let 。guard可以判断空值, 可以类型等,让我们预先做处理。
class ListItem {
var icon: UIImage?
var title: String = ""
var url: NSURL!
static func listItemsFromJSONData(jsonData: NSData?) -> [ListItem] {
guard let nonNilJsonData = jsonData,
let json = try? NSJSONSerialization.JSONObjectWithData(nonNilJsonData, options: []),
let jsonItems = json as? Array<NSDictionary>
else {
// If we failed to unserialize the JSON
// or that JSON wasn't an Array of NSDictionaries,
// then bail early with an empty array
//就是guard 让我们预先对数据空,或者正确的情况做判断和处理。
return []
}
var items = [ListItem]() // 用swift的写法来代替oc
for itemDesc in jsonItems {
let item = ListItem()
if let icon = itemDesc["icon"] as? String {
item.icon = UIImage(named: icon)
}
if let title = itemDesc["title"] as? String {
item.title = title
}
if let urlString = itemDesc["url"] as? String, let url = NSURL(string: urlString) {
item.url = url
}
items.append(item)
}
return items
}
}
guard 非常好用,因为,我们可以在函数开始的时候,预先检查 数据是否合法,在接下来的code中就不需要担心这些检查问题。
6. swift比oc繁琐?
上边的代码看起来是这样,swift比oc代码量更多。但是这只是因为oc没有做很多安全检查。oc代码可能在很多情况下崩溃比如:1.json不合法,2.json中数据类型不对等等。在oc中,我们忘记去处理这些情况。swift强制我们处理这些情况。
7. 结论
swift是按照更安全的理念被设计的,所以不要通过使用! 强制解包的方式来处理 可选值。如果你发现!, 你需要想想是否哪里出了错。
小note
// 1. 使用try? 的时候, 如果你不关心是什么原因导致的nil,可以直接用try?,如果你想要知道为何会返回nil,最好使用 do { try... } catch { }
//2. 在最后一段代码中,保留了一个 ! 是 return items.copy() as! NSArray。 一些情况下,使用!是可以的,如果你真的真的知道值不是空的或者类型确定。