Swift 3.1的新改动

作者: BYQiu | 来源:发表于2017-03-30 11:01 被阅读190次

    Xcode 8.3Swift 3.1 已经发布了(3/28)!

    可以通过 AppStoreApple Developer 进行下载

    Xcode 8.3 优化了 Objective-C 与 Swift 混编项目的编译速度.

    Swift 3.1 版本包含一些期待已久的 Swift package manager 功能和语法本身的改进。

    如果您没有密切关注 Swift Evolution 进程,请继续阅读 - 本文非常适合您!

    在本文中,我将强调Swift 3.1中最重要的变化,这将对您的代码产生重大影响。我们来吧!😃

    开始

    Swift 3.1与Swift 3.0源代码兼容,因此如果您已经使用Xcode 中的 Edit \ Convert \ To Current Swift Syntax ... 将项目迁移到Swift 3.0,新功能将不会破坏您的代码。不过,苹果已经在Xcode 8.3中支持Swift 2.3。所以如果你还没有从Swift 2.3迁移,现在是时候这样做了!

    在下面的部分,您会看到链接的标签,如[SE-0001]。这些是 Swift Evolution 提案号码。我已经列出了每个提案的链接,以便您可以发现每个特定更改的完整详细信息。我建议您尝试在Playground上验证新的功能,以便更好地了解所有更改的内容。

    Note:如果你想了解 swift 3.0 中的新功能,可以看这篇文章

    语法改进

    首先,我们来看看这个版本中的语法改进,包括关于数值类型的可失败构造器Failable Initializers),新的序列函数等等。

    可失败的数值转换构造器(Failable Numeric Conversion Initializers)

    Swift 3.1 为所有数值类型 (Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Float80, Double) 添加了可失败构造器

    这个功能非常有用,例如,以安全、可恢复的方式处理外源松散类型数据的转换,下面来看 Student 的 JSON 数组的处理:

    class Student {
      let name: String
      let grade: Int
     
      init?(json: [String: Any]) {
        guard let name = json["name"] as? String,
              let gradeString = json["grade"] as? String,
              let gradeDouble = Double(gradeString),
              let grade = Int(exactly: gradeDouble)  // <-- 3.1 的改动在这
        else {
            return nil
        }
        self.name = name
        self.grade = grade
      }
    }
     
    func makeStudents(with data: Data) -> [Student] {
      guard let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),
            let jsonArray = json as? [[String: Any]] else {
        return []
      }
      return jsonArray.flatMap(Student.init)
    }
     
    let rawStudents = "[{\"name\":\"Ray\", \"grade\":\"5.0\"}, {\"name\":\"Matt\", \"grade\":\"6\"},
                        {\"name\":\"Chris\", \"grade\":\"6.33\"}, {\"name\":\"Cosmin\", \"grade\":\"7\"}, 
                        {\"name\":\"Steven\", \"grade\":\"7.5\"}]"
    let data = rawStudents.data(using: .utf8)!
    let students = makeStudents(with: data)
    dump(students) // [(name: "Ray", grade: 5), (name: "Matt", grade: 6), (name: "Cosmin", grade: 7)]
    

    Student 类中使用了一个可失败构造器将 grade 属性从 Double 转变为 Int,像这样

    let grade = Int(exactly: gradeDouble)
    

    如果gradeDouble不是整数,例如6.33,它将失败。如果它可以用一个正确的表示Int,例如6.0,它将成功。

    Note:虽然throwing initializers 可以用来替代 failable initializers。但是使用 failable initializers 会更好,更符合人的思维。

    新的序列函数(Sequence Functions)

    swift3.1添加了两个新的标准库函数在 Sequence 协议中:prefix(while:)``和prefix(while:)[SE-0045]

    构造一个斐波纳契无限序列:

    let fibonacci = sequence(state: (0, 1)) {
      (state: inout (Int, Int)) -> Int? in
      defer {state = (state.1, state.0 + state.1)}
      return state.0
    }
    

    在Swift 3.0中,您只需指定迭代次数即可遍历fibonacci序列:

    // Swift 3.0
    for number in fibonacci.prefix(10) {
      print(number)  // 0 1 1 2 3 5 8 13 21 34
    }
    

    在swift 3.1中,您可以使用prefix(while:)drop(while:)获得符合条件在两个给定值之间的序列中的所有元素,就像这样:

    // Swift 3.1
    let interval = fibonacci.prefix(while: {$0 < 1000}).drop(while: {$0 < 100})
    for element in interval {
      print(element) // 144 233 377 610 987
    }
    

    prefix(while:)返回满足某个谓词的最长子序列。它从序列的开头开始,并停在给定闭包返回false的第一个元素上。

    drop(while:) 相反:它返回从给定关闭返回false的第一个元素开始的子序列,并在序列结尾完成。

    Note:这种情况,可以使用尾随闭包的写法:

    let interval = fibonacci.prefix{$0 < 1000}.drop{$0 < 100}
    

    Concrete Constrained Extensions(姑且翻译为类的约束扩展吧)

    Swift 3.1允许您扩展具有类型约束的通用类型。以前,你不能像这样扩展类型,因为约束必须是一个协议。我们来看一个例子。

    例如,Ruby on Rails提供了一种isBlank检查用户输入的非常有用的方法。以下是在Swift 3.0中用 String 类型的扩展实现这个计算型属性

    // Swift 3.0
    extension String {
      var isBlank: Bool {
        return trimmingCharacters(in: .whitespaces).isEmpty
      }
    }
     
    let abc = " "
    let def = "x"
     
    abc.isBlank // true
    def.isBlank // false
    

    如果你希望isBlank计算型属性为一个可选值所用,在swift 3.0中,你将要这样做

    // Swift 3.0
    protocol StringProvider {
      var string: String {get}
    }
     
    extension String: StringProvider {
      var string: String {
        return self
      }
    }
     
    extension Optional where Wrapped: StringProvider {
      var isBlank: Bool {
        return self?.string.isBlank ?? true
      }
    }
     
    let foo: String? = nil
    let bar: String? = "  "
    let baz: String? = "x"
     
    foo.isBlank // true
    bar.isBlank // true
    baz.isBlank // false
    

    这创建了一个采用 StringStringProvider 协议而在你使用StringProvider扩展可选的 wrapped 类型时,添加isBlank方法。

    Swift 3.1中,用来替代协议方法,扩展具体类型的方法像这样:

    // Swift 3.1
    extension Optional where Wrapped == String {
      var isBlank: Bool {
        return self?.isBlank ?? true
      }
    }
    

    这就用更少的代码实现了和原先相同的功能~

    泛型嵌套(Nested Generics)

    Swift 3.1允许您将嵌套类型与泛型混合。作为一个练习,考虑这个(不是太疯狂)的例子。每当某个团队领导raywenderlich.com想在博客上发布一篇文章时,他会分配一批专门的开发人员来处理这个问题,以满足网站的高质量标准:

    class Team<T> {
      enum TeamType {
        case swift
        case iOS
        case macOS
      }
     
      class BlogPost<T> {
        enum BlogPostType {
          case tutorial
          case article
        }
     
        let title: T
        let type: BlogPostType
        let category: TeamType
        let publishDate: Date
     
        init(title: T, type: BlogPostType, category: TeamType, publishDate: Date) {
          self.title = title
          self.type = type
          self.category = category
          self.publishDate = publishDate
        }
      }
     
      let type: TeamType
      let author: T
      let teamLead: T
      let blogPost: BlogPost<T>
     
      init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost<T>) {
        self.type = type
        self.author = author
        self.teamLead = teamLead
        self.blogPost = blogPost
      }
    }
    

    BlogPost内部类嵌套在其对应的Team外部类中,并使两个类都通用。这是团队如何寻找我在网站上发布的教程和文章:

    Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix", 
         blogPost: Team.BlogPost(title: "Pattern Matching", type: .tutorial, 
         category: .swift, publishDate: Date()))
     
    Team(type: .swift, author: "Cosmin Pupăză", teamLead: "Ray Fix", 
         blogPost: Team.BlogPost(title: "What's New in Swift 3.1?", type: .article, 
         category: .swift, publishDate: Date()))
    

    但实际上,在这种情况下,您可以简化该代码。如果嵌套的内部类型使用通用外部类型,那么它默认继承父类的类型。因此,您不需要如此声明:

    class Team<T> {
      // original code 
     
      class BlogPost {
        // original code
      }  
     
      // original code 
      let blogPost: BlogPost
     
      init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost) {
        // original code   
      }
    }
    

    Note:如果您想了解更多关于Swift中的泛型,请阅读我们最近更新的Swift泛型入门的教程

    Swift版本的可用性

    您可以使用#if swift(>= N) 静态构造来检查特定的Swift版本:

    // Swift 3.0
    #if swift(>=3.1)
      func intVersion(number: Double) -> Int? {
        return Int(exactly: number)
      }
    #elseif swift(>=3.0)
      func intVersion(number: Double) -> Int {
        return Int(number)
      }
    #endif
    

    然而,当使用Swift标准库时,这种方法有一个主要缺点。它需要为每个受支持的旧语言版本编译标准库。这是因为当您以向后兼容模式运行Swift编译器时,例如您要使用Swift 3.0行为,则需要使用针对该特定兼容性版本编译的标准库版本。如果您使用版本3.1模式编译的,那么您根本就没有正确的代码

    因此,@available除了现有平台版本 [SE-0141] 之外,Swift 3.1扩展了该属性以支持指定Swift版本号:

    // Swift 3.1
     
    @available(swift 3.1)
    func intVersion(number: Double) -> Int? {
      return Int(exactly: number)
    }
     
    @available(swift, introduced: 3.0, obsoleted: 3.1)
    func intVersion(number: Double) -> Int {
      return Int(number)
    }
    

    这个新功能提供了与intVersionSwift版本有关的方法相同的行为。但是,它只允许像标准库这样的库被编译一次。编译器然后简单地选择可用于所选择的给定兼容性版本的功能。

    Note:注意:如果您想了解更多关于Swift 的可用性属性( availability attributes),请参阅我们关于Swift中可用性属性的教程

    逃逸闭包(Escaping Closures)

    在Swift 3.0 [ SE-0103 ] 中函数中的闭包的参数是默认是不逃逸的(non-escaping)。在Swift 3.1中,您可以使用新的函数withoutActuallyEscaping()将非逃逸闭包转换为临时逃逸。

    func perform(_ f: () -> Void, simultaneouslyWith g: () -> Void,
                 on queue: DispatchQueue) {
      withoutActuallyEscaping(f) { escapableF in     // 1
        withoutActuallyEscaping(g) { escapableG in
          queue.async(execute: escapableF)           // 2
          queue.async(execute: escapableG)     
     
          queue.sync(flags: .barrier) {}             // 3
        }                                            // 4
      }
    }
    

    此函数同时加载两个闭包,然后在两个完成之后返回。

    1. fg 进入函数后由非逃逸状态,分别转换为逃逸闭包:escapableFescapableG
    2. async(execute:) 的调用需要逃逸闭包,我们在上面已经进行了转换。
    3. 通过运行sync(flags: .barrier),您确保async(execute:)方法完全完成,稍后将不会调用闭包。
    4. 在范围内使用 escapableF and escapableG.

    如果你存储临时逃离闭包(即真正逃脱)这将是一个Bug。未来版本的标准库可以检测这个陷阱,如果你试图调用它们。

    Swift Package Manager 更新

    啊,期待已久的 Swift Package Manage 的更新了!

    可编辑软件包(Editable Packages)

    Swift 3.1将可编辑软件包(editable packages)的概念添加到Swift软件包管理器 [ SE-0082 ]

    swift package edit命令使用现有的Packages并将其转换为editable Packages。使用--end-edit命令将 package manager 还原回 规范解析的软件包(canonical resolved packag)

    版本固定(Version Pinning)

    Swift 3.1 添加了版本固定的概念[ SE-0145 ]。该 pin 命令 固定一个或所有依赖关系如下所示:

    $ swift package pin --all      // 固定所有的依赖
    $ swift package pin Foo        // 固定 Foo 在当前的闭包
    $ swift package pin Foo --version 1.2.3  // 固定 Foo 在 1.2.3 版本
    
    

    使用unpin命令恢复到以前的包版本:

    $ swift package unpin —all
    $ swift package unpin Foo
    

    Package manager 将每个依赖库的版本固定信息存储在 Package.pins 文件中。如果该文件不存在,则Package manager 会自动创建。

    其他

    swift package reset 命令将会把 Package 重置干净。

    swift test --parallel 命令 执行测试。

    其他改动

    在 swift 3.1 中还有一些小改动

    多重返回函数

    C函数返回两次,例如vforkvfork已经不用了。他们以有趣的方式改变了程序的控制流程。所以 Swift 社区 已经禁止了该行为,以免导致编译错误。

    自动链接失效(Disable Auto-Linking)

    Swift Package Manager 禁用了在C语言 模块映射(module maps)中的自动链接的功能:

    // Swift 3.0
    module MyCLib {
        header “foo.h"
        link “MyCLib"
        export *
    }
     
    // Swift 3.1
    module MyCLib {
        header “foo.h”
        export *
    }
    

    结语

    Swift 3.1改善了Swift 3.0的一些功能,为即将到来的Swift 4.0的大改动做准备。这些包括对泛型,正则表达式,更科学的String等方面的作出极大的改进。

    如果你想了解更多,请转到 Swift standard library diffs 或者查看官方的的Swift CHANGELOG,您可以在其中阅读所有更改的信息。或者您可以使用它来了解 Swift 4.0 中的内容!

    声明

    本文译自 What’s New in Swift 3.1?
    @柏荧(BY) 进行翻译,转载请保留原文链接.

    相关文章

      网友评论

        本文标题:Swift 3.1的新改动

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