大纲如下:
- Fundamentals
- Pattern Matching
- Availability Checking
- Protocol Extensions
- Error Handling
Fundamentals
枚举的反射
Swift1.2 :
enum Animals{
case Dog, Cat, Troll, Dragon
}
let a = Animals.Dragon
print(a) //打印 (Enum Value)
Swift2. 0:
enum Animals{
case Dog, Cat, Troll, Dragon
}
let a = Animals.Dragon
print(a) //打印 (Animals.Dragon)
调试过程中更直观地显示数据。
枚举中的关联值
Either:两者选其一,非此即彼的意思。
这里构造一个 Either 枚举,且绑定 T1 和 T2 两个泛型。Either枚举有两种情况:要么就是First,关联值为 T1 类型;要么就是 Second ,关联值为 T2 类型。想法很nice,但是现实很残酷,swift1.2中无法编译通过,往往解决方法是自己构造一个Box类。
swift1.2 :
enum Either{
case First(T1)
case Second(T2)
}
// swift1.2中直接报错
swift2.0 :
enum Either{
case First(T1)
case Second(T2)
}
// 完美执行
枚举中的递归
swift1.2 中不允许采用递归,所以使用以下方式编译报错:
swift1.2 :
enum Tree{
case Leaf(T)
case Node(Tree,Tree)
}
// 编译器报错,通过解决方式是自定义一个Box类
swift2.0 :
enum Tree{
case Leaf(T)
indirect case Node(Tree,Tree)
//在使用递归枚举用例的地方使用修饰符 indirect
}
repeat-while 替换 do-whi2
可能是因为引入了 do-catch 语句,为了避免混淆,所以将其改成了 repeat-while
声明,感觉有一定意义吧。
Option Sets
Swift1 中讨论最多的就是使用位掩码(bit mask)来实现,学过C语言的朋友应该再熟悉不过。
swift1 :
// 设置动画选项时如此声明,表示该动画具有 重复、渐进渐出以及卷曲动画 三个属性
viewAnimationOptions = .Repeat | .CurveEaseIn | .TransitionCurlUp
但是往往会造成误解:
viewAnimationOptions = .Repeat | .CurveEaseIn | .TransitionCurlUp
viewAnimationOptions = nil
if viewAnimationOptions & .TransitionCurlUp != nil {}
采用与(&)、或(|)操作来进行结合,美名其曰是对集合进行操作,实际不过是C语言的位操作罢了。甚至类型上也显得很牵强,要知道 options 是一个位掩码值,做一个与(&)操作后竟然可以等于nil(恰当地处理应该为一个空集合,不是吗?)。
swift2.0 :
// 采用中括号[]来包括,让其看起来是对集合进行操作,倘若没有元素,即[]空集合 而非无值nil!
viewAnimationOptions = [.Repeat, .CurveEaseIn, .TransitionCurlUp]
viewAnimationOptions = []
// contain操作就是简单.TransitionCurlUp 是否包含于集合中 很形象!
if viewAnimationOptions.contains(.TransitionCurlUp) {
自定义一个可选集合:
struct MyFontStyle : OptionSetType {
// 一定要有rawValue
let rawValue : Int
static let Bold = MyFontStyle(rawValue: 1)
static let Italic = MyFontStyle(rawValue: 2)
static let Underline = MyFontStyle(rawValue: 4)
static let Strikethrough = MyFontStyle(rawValue: 8)
}
// style 是MyFontStyle类型 本例构造了一个集合
myFont.style = []
myFont.style = [.Underline]
myFont.style = [.Bold, .Italic]
if myFont.style.contains(.StrikeThrough) {}
更多请见我写的这篇文章[Swift2.0系列]OptionSetType使用
函数和方法
swift1.2 :
// 这里的save是全局函数(function)
func save(name: String, encrypt: Bool) { ... }
// 类中的save是方法(method)
class Widget {
func save(name: String, encrypt: Bool) { ... }
// 调用全局函数 和 类方法
save("thing", false)
widget.save("thing", encrypt: false)
注意调用函数和方法的标签,函数是没有任何外部标签的;而方法除了第一个没有外部标签,从第二个开始都带标签。
swift2.0为了和oc统一,现在函数也是从第二个参数开始默认给定外部标签。
swift2.0 :
func save(name: String, encrypt: Bool) { ... }
class Widget {
func save(name: String, encrypt: Bool) { ... }
// 注意这里 函数也是带标签了
save("thing", encrypt: false)
widget.save("thing", encrypt: false)
你可能好奇为什么第一个参数没有外部标签的,因为编译器自动用下划线_
替换掉了第一个参数的外部标签,表示省略外部标签,在调用时是不会显示出来的,就像这样:
// 注意_ 和 name之间是有空格的。 _表示忽略外部标签, name表示函数内部使用参数标签
// 而encrypt 默认外部标签和内部标签是为同一个,即encrypt
func save(_ name: String, encrypt: Bool)
// 还可以这么写
func save(_ name: String,encrypt encrypt: Bool)
// 显然写两个encrypt 毫无意义,除非你想自定义外部参数标签名称
// 想要第一个参数也具有外部参数标签名,可以这么干
func save(externalName name: String,encrypt encrypt: Bool)
关于Diagnostics
swift2.0中对警告和错误更为敏感和友好,不像swift1.2中模糊不清的错误信息,而是“一针见血”,方便你“对症下药”!当然swift2.0还新增了一些其他有用的经过,譬如变量声明却没有使用,或者变量仅作为常量即可,无须使用var
关键字声明为变量等等
Pattern Matching
**swift1.0 **很容易出现金字塔“鞭尸”的情况:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let dest = segue.destinationViewController as? BlogViewController
if let blogIndex = tableView.indexPathForSelectedRow()?.row {
if segue.identifier == blogSegueIdentifier {
dest.blogName = swiftBlogs[blogIndex]
...
...
}
}
}
}
swift1.2 中我们可以采用条件符合的写法,但是也不尽如意:
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// 注意我们把三个条件整合成了一个if语句 但是还是逃脱不了被鞭尸
if let dest = segue.destinationViewController as? BlogViewController
let blogIndex = tableView.indexPathForSelectedRow()?.row
where segue.identifier == blogSegueIdentifier {
dest.blogName = swiftBlogs[blogIndex]
...
...
}
}
再举个Json解析的例子:
// 返回Either枚举值 要么就是有值为.First(Person) 要么没有值.Second(String)
func process(json: AnyObject) -> Either {
// 还有一点值得注意 我们声明的name 和 year 都是可选类型值 所以都是需要解包的
let name: String? = json["name"] as? String
if name == nil {
return .Second("missing name")
}
let year: Int? = json["year"] as? Int
if year == nil {
return .Second("missing year")
}
let person = processPerson(name!, year!)// 解包
return .First(person)
显然解包行为的代码令人无法直视,所以喽你可能会在声明name 和 year就直接采用隐式可选类型,即使用String!
和Int!
来干,只能说治标不治本吧。ps:记住显示可选类型和隐式可选类型都是可选类型,意味着它们都有值不存在的情况,即nil
;区别在于显示可选类型要解包,隐式可选类型的解包由编译器帮你完成,换句话说你不用每次都使用!
来解包拉,但是这样造成的严重后果是倘若值为nil了,那么程序就crash喽,所以且码且谨慎。
swift2.0 引入了guard
声明,意为警卫,充当一个保护者,只有条件满足时才可以执行下面的程序;倘若不满足,就必须提前突出喽!你可以使用return break 等关键字,当然也可以使用no-return 方式,譬如assert exception等等。
// 现在是不是简洁很多了 值得一提的是 name year 的作用域是整个函数
// 而if-let 中的解包数据作用域只能是if作用域
func process(json: AnyObject) -> Either {
guard let name = json["name"] as? String,
let year = json["year"] as? Int else
return .Second(“bad input”)
}
let person = processPerson(name, year)
return .First(person)
}
再来谈谈 switch 语句,我很喜欢swift中的switch用法,可以使用符合条件操作等,但是有一点我也很讨厌,当我想要匹配的条件仅有一个的时候,竟然还要写switch(){//一堆东西}
,就像下面这样:
// 根据 bar() 方法返回值进行switch匹配 执行对应操作
switch bar() {
case .MyEnumCase(let value):where value != 42:
doThing(value)
default: break
}
swift2.0 新增匹配方式:
if case .MyEnumCase(let value) = bar() where value != 42 {
doThing(value)
}
for-in中的匹配
swift1.2 前你是否遇到过这种情况:
// for-in 每一次循环都要执行 value != ""的判断 是否令你不爽
for value in mySequence {
if value != "" {
doThing(value)
}
}
你有考虑过这么干吗:
for value in mySequence where value != "" {
doThing(value)
}
假设 mySequence 是一个枚举序列呢??? 难道你要在for-in中每一个循环执行一次switch
匹配吗? Swift2.0中对这些say goodbye 吧!
for case .MyEnumCase(let value) in enumValues {
doThing(value)
}
Availability Checking
请见这篇文章:
[Swift2.0系列]API可用性检查(译)
Protocol Extensions
给已实现的类型进行Extension 想必很多开发者都尝试过,譬如为Array扩展一个countIf
方法:
extension Array{
func countIf(match:Element -> Bool) -> Int{
var n = 0
for value in self{
if match(value){ n++}
}
return n
}
}
这种做法很常见,但你要知道并不是只有Array适用countIf方法的,其他序列同样适用!所以喽我们会声明一个全局函数专门对传入的序列进行处理:
func countIf(collection:T,
match:T.Generator.Element -> Bool) -> Int{
var n = 0
// WWDC pdf 为 for value in self 不知是否错误 我改为 collection
for value in collection{
if match(value){ n++}
}
return n
}
但是通过全局函数来处理指定队列可能会让初学者很迷茫,他怎么知道有filter map这些函数呢?事实上swift1.0就是那么干的! 当时用得相当不爽。
// 这种方式糟透了!
let x = filter(map(numbers) { $0 * 3 }) { $0 >= 0 }
swift2.0 :
let x = numbers.map { $0 * 3 }.filter { $0 >= 0 }
我们所要做的是对CollectionType协议进行Extension,即为该协议实现默认方法:
extension CollectionType {
func countIf(match: Element -> Bool) -> Int {
var n = 0
for value in self {
if match(value) { n++ }
}
return n
}
}
当然你可以在Array中覆盖次默认实现,而采用自定义实现。
请见这篇文章:
[Swift2.0系列]Protocol Extensions 基础用法和实战(初稿)
Error Handling
请见我写的这篇文章:
[Swift2.0系列]Error Handling(基础语法篇)
[Swift2.0系列]Error Handling(项目应用篇)