Swift 3.1新改动

概述

Swift 3.1 和 Swift 3.0 是源码兼容的,所以如果已经使用 Edit\Convert\To Current Swift Syntax… 将项目迁移到了 Swift 3.0 的话,新功能将不会破坏我们的代码。不过,苹果在 Xcode 8.3 中已经抛弃了对 Swift 2.3 的支持。所以如果还没有从 Swift 2.3 迁移过来,现在要抓紧做了!

1. 可失败数值转换初始化方法

Swift 3.1 为所有数字类型 (Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Float80, Double) 实现了可失败初始化方法,要么完全成功、不损失精度,要么返回 nil 。

例如以下处理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,就像这样:
```swift
let grade = Int(exactly: gradeDouble)```
如果 gradeDouble 是小数,例如 6.33,就会失败。如果可以用 Int 来表示,例如 6.0,就会成功。

##2. 新的序列函数

Swift 3.1 为标准库的 Sequence 协议增加了两个新函数,用于数据过滤:`prefix(while:)` 和` drop(while:)`

Swift 3.1 允许我们使用 prefix(while:) 和 drop(while:) 来获取位于序列两个给定值之间所有的元素,像这样:
```swift
// 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:) `返回满足某 predicate 的最长子序列。从序列的开头开始,并且在第一个从给定闭包中返回 false 的元素处停下。

`drop(while:) `做相反的操作:从第一个在闭包中返回 false 的元素开始,直到序列的结束,返回此子序列。

##3. Concrete Constrained Extensions

Swift 3.1 允许我们扩展具有 concrete type constraint 的泛型。之前不能扩展这样的类型,因为约束必须是一个协议。
例如,Ruby On Rails 提供了一个非常实用的方法 isBlank 用于检查用户输入。在 Swift 3.0 中我们会将其实现为 String 扩展中的计算属性:
```swift
// Swift 3.0
extension String {
  var isBlank: Bool {
    return trimmingCharacters(in: .whitespaces).isEmpty
  }
}

let abc = " "
let def = "x"

abc.isBlank // true
def.isBlank // false
如果想要 string 可选值 也能用 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```
我们创建了一个 StringProvider 协议供 String 采用。当拆包类型是` StringProvider `的时候使用它扩展 Optional,添加 `isBlank `方法。

Swift 3.1 可以用这样的协议来扩展 concrete type:
```swift
// Swift 3.1
extension Optional where Wrapped == String {
  var isBlank: Bool {
    return self?.isBlank ?? true
  }
}```

##4. 泛型嵌套
Swift 3.1 让我们可以混合使用泛型和类型嵌套。练习一下,看看这个(不是很难的)例子。如果某个 raywenderlich.com 的团队领导想要在博客上发一篇文章,他会找专门的开发者团队来处理这个问题,以保证文章的质量:
```swift
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`中,这两个类都是泛型。目前团队在寻找已发布的教程和文章时需要这样做:
```swift
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()))```

但实际上可以简化这里的代码。如果嵌套的内部类型用了外部的泛型,它就默认继承了父类的类型。因此我们不需要声明,只要这样写就可以了:
```swift
class Team<T> {
  // 本来的代码

  class BlogPost {
    // 本来的代码
  }  

  // 本来的代码
  let blogPost: BlogPost

  init(type: TeamType, author: T, teamLead: T, blogPost: BlogPost) {
    // 本来的代码   
  }
}```

>**注意:**如果想学习 Swift 中的**泛型**,读一读这篇最近更新的教程 [getting started with Swift generics](https://www.raywenderlich.com/154371/swift-generics-tutorial-getting-started) 。

##5. 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 3.0 的行为,则需要使用针对该版本编译的标准库。如果使用 3.1 版本的标准库,就根本没有正确的代码。
所以,Swift 3.1 扩展了 `@available`属性 
```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)
}```

这个新功能与 `intVersion` 方法相同。但是,它只允许像标准库这样的库被编译一次。编译器随后只要选择与对应版本兼容的功能即可。
>**注意:**如果想学习 Swift 的 **availability attributes**,看看这篇教程 [availability attributes in Swift](https://www.raywenderlich.com/139077/availability-attributes-swift)。


##6. Swift 包管理器的更新

- **Editable Packages**
Swift 3.1 在 [Swift 包管理器](https://github.com/apple/swift-package-manager) 中新增了 **Editable Packages** 概念 
`swift package edit`命令可以将现有包转换为可编辑的。可编辑的包将会替换 **dependency graph** 中的规范包。使用 `—end-edit`
 命令将包管理器还原回**规范解析的包**。
- **Version Pinning**
Swift 3.1 在特定版本的 [Swift 包管理器](https://github.com/apple/swift-package-manager) 中新增了 **version pinning** 概念。 `pin`命令会像这样固定一个或多个依赖:

$ swift package pin --all // pin 所有依赖
$ swift package pin Foo // 把 Foo pin 在当前解析版本
$ swift package pin Foo --version 1.2.3 // 把 Foo pin 在 1.2.3```

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

$ swift package unpin —all$ swift package unpin Foo

包管理器在 Package.pins 中存储每个包的活跃版本 pin 信息。如果文件不存在,包管理器则会按照包 manifest 中指定的要求自动创建该文件,这是 automatic pinning 过程的一部分。

  • 其它
    swift package reset命令将包重置为干净状态,不会检出当前的依赖关系或 build artifact。还有,使用 swift test --parallel命令并行执行测试。

==

参考文献
英文原版:What’s New in Swift 3.1?
一篇文章帮你彻底了解 Swift 3.1 的新内容 感谢翻译🙏

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容