WWDC 2015 - Session 106 - What's New In Swift2.0

大纲如下:

  • 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(项目应用篇)

你可能感兴趣的:(WWDC 2015 - Session 106 - What's New In Swift2.0)