美文网首页关于WWDC的故事...iOS
WWDC 2015 - Session 106 - What&#

WWDC 2015 - Session 106 - What&#

作者: NinthDay | 来源:发表于2016-03-12 14:57 被阅读106次

    大纲如下:

    • 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<T1,T2>{
      case First(T1)
      case Second(T2)
    }
    // swift1.2中直接报错
    

    swift2.0 :

    enum Either<T1,T2>{
      case First(T1)
      case Second(T2)
    }
    // 完美执行
    

    枚举中的递归

    swift1.2 中不允许采用递归,所以使用以下方式编译报错:

    swift1.2 :

    enum Tree<T>{
      case Leaf(T)
      case Node(Tree,Tree)
    }
    // 编译器报错,通过解决方式是自定义一个Box类
    

    swift2.0 :

    enum Tree<T>{
      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<Person,String> { 
    // 还有一点值得注意 我们声明的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<Person,String> {
        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<T:CollectionType>(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

    请见我写的这篇文章:

    相关文章

      网友评论

        本文标题:WWDC 2015 - Session 106 - What&#

        本文链接:https://www.haomeiwen.com/subject/tnuxlttx.html