好消息:Swift 4.2 现已在 Xcode 10 beta 中提供!此版本更新重要的 Swift 4.1 功能,并改进语言以准备 ABI 稳定性。
本教程介绍了 Swift 4.2 中最重要的变化。它需要 Xcode 10,因此请确保你在开始之前下载并安装 Xcode 的最新测试版。
前言
Swift 4.2 与 Swift 4.1 的源代码兼容,但与任何其他版本不兼容。 Apple 设计的 Swift 4.2 是在 Swift 5 中实现 ABI 稳定性的中间步骤,它应该能够在不同 Swift 版本编译的应用和库中保持二进制兼容。在集成到最终的 ABI 之前,ABI 功能会有大量的时间来获得来自社区的反馈。
本教程的部分包含 Swift Evolution 提案编号,如 [SE-0001] 。你可以通过点击每个提案的链接标签来浏览每个更改的详细信息。
let playlist = ["Nothing Else Matters", "Stairway to Heaven", "I Want to Break Free", "Yesterday"] let index =Int(arc4random_uniform(UInt32(playlist.count))) let song = playlist[index]
Swift 4.1 使用 arc4random_uniform(_:) 从 playlist 生成一个有效的索引(index)并返回相应的 song。 这个解决方案需要在 Int 和 UInt32 之间转换,并且还有所有前面提到的问题。
classPerson { let name: String let age: Int privatelet details: [String: String] init(name: String, age: Int, details: [String: String]) { self.name = name self.age = age self.details = details } subscript(key: String) -> String { switch key { case"info": return"\(name) is \(age) years old." default: return details[key] ??"" } } }
let details = ["title": "Author", "instrument": "Guitar"] let me =Person(name: "Cosmin", age: 32, details: details) me["info"] // "Cosmin is 32 years old." me["title"] // "Author"
在这种情况下,下标将根据个人的姓名和年龄从私人数据存储或自定义消息中返回内容。
Swift 4.2 使用动态成员查找(dynamic member lookup)为下标提供点语法,而不是 [SE-0195]:
Swift 4.1 默认情况下不提供对枚举案例的访问。这样就留下了一些相当不雅的解决方案,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
enumSeasons: String { case spring ="Spring", summer ="Summer", autumn ="Autumn", winter ="Winter" }
enumSeasonType { case equinox case solstice }
let seasons = [Seasons.spring, .summer, .autumn, .winter] for (index, season) in seasons.enumerated() { let seasonType = index %2==0?SeasonType.equinox : .solstice print("\(season.rawValue)\(seasonType).") }
在这里,将 Seasons 添加到 seasons,并循环访问数组以获取每个季节的名称和类型。
但 Swift 4.2 可以做的更好!
Swift 4.2 将枚举案例数组添加到枚举中 [SE-0194]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 1 enumSeasons: String, CaseIterable { case spring ="Spring", summer ="Summer", autumn ="Autumn", winter ="Winter" }
enumSeasonType { case equinox case solstice }
// 2 for (index, season) inSeasons.allCases.enumerated() { let seasonType = index %2==0?SeasonType.equinox : .solstice print("\(season.rawValue)\(seasonType).") }
以下是如何在 Swift 4.2 中完成同样的事情:
使 Seasons 继承 CaseIterable 以创建枚举案例。
循环浏览所有 case 并打印每个季节的名称和类型。
可以选择仅将某些情况添加到枚举案例数组中:
1 2 3 4 5 6 7
enumMonths: CaseIterable { case january, february, march, april, may, june, july, august, september, october, november, december staticvar allCases: [Months] { return [.june, .july, .august] } }
在这里只添加夏季月份,因为它们是一年中最阳光的季节!
如果枚举包含 unavailable 的元素,则应手动将所有 available 添加到数组中:
1 2 3 4 5 6 7 8 9 10
enumDays: CaseIterable { case monday, tuesday, wednesday, thursday, friday
iflet firstTeen = ages.first(where: { $0.hasSuffix("teen") }), let firstIndex = ages.index(where: { $0.hasSuffix("teen") }), let firstMajorIndex = ages.index(of: "eighteen") { print("Teenager number \(firstIndex +1) is \(firstTeen) years old.") print("Teenager number \(firstMajorIndex +1) isn't a minor anymore.") } else { print("No teenagers around here.") }
Swift 4.1 的做法是使用 first(where:) 来查找第一个青少年的年龄(ages),index(where:) 为第一个青少年的索引,index(of:) 为第一个18岁的青少年的索引 。
Swift 4.2重新命名了这些方法中的一部分以实现一致性[SE-0204]:
1 2 3 4 5 6 7 8
iflet firstTeen = ages.first(where: { $0.hasSuffix("teen") }), let firstIndex = ages.firstIndex(where: { $0.hasSuffix("teen") }), let firstMajorIndex = ages.firstIndex(of: "eighteen") { print("Teenager number \(firstIndex +1) is \(firstTeen) years old.") print("Teenager number \(firstMajorIndex +1) isn't a minor anymore.") } else { print("No teenagers around here.") }
Swift 4.1 也没有定义任何 Collection 方法来查找某个元素的最后一个索引或匹配给定谓词的最后一个元素。以下是 4.1 中的处理方法:
1 2 3 4 5 6 7 8 9 10 11 12
// 1 let reversedAges = ages.reversed()
// 2 iflet lastTeen = reversedAges.first(where: { $0.hasSuffix("teen") }), let lastIndex = reversedAges.index(where: { $0.hasSuffix("teen") })?.base, let lastMajorIndex = reversedAges.index(of: "eighteen")?.base { print("Teenager number \(lastIndex) is \(lastTeen) years old.") print("Teenager number \(lastMajorIndex) isn't a minor anymore.") } else { print("No teenagers around here.") }
iflet lastTeen = ages.last(where: { $0.hasSuffix("teen") }), let lastIndex = ages.lastIndex(where: { $0.hasSuffix("teen") }), let lastMajorIndex = ages.lastIndex(of: "eighteen") { print("Teenager number \(lastIndex +1) is \(lastTeen) years old.") print("Teenager number \(lastMajorIndex +1) isn't a minor anymore.") } else { print("No teenagers around here.") }
// 4 let swift41Tutorial =Tutorial(title: "What's New in Swift 4.1?", author: "Cosmin Pupăză") let swift42Tutorial =Tutorial(title: "What's New In Swift 4.2?", author: "Cosmin Pupăză") let swift41Screencast =Screencast(author: "Jessy Catterwaul", tutorial: swift41Tutorial) let swift42Screencast =Screencast(author: "Jessy Catterwaul", tutorial: swift42Tutorial) let sameScreencast = swift41Screencast == swift42Screencast
如果 if 条件满足,这里会检查 instruments 是否能实现了 Tuneable,然后调用 tune()方法。在这里例子中,数组不能转化为 Tuneable,因为 Instrument 类型不满足 Tuneable。如果你创建有两个 Keyboard 元素的数组,则 if 条件会满足,并且会调用 tune() 方法。
标准库中哈希一致性改进
可选项,数组,字典和范围(range)在 Swift 4.2 中是 Hashable,当它们的元素是 Hashable 时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
structChord: Hashable { let name: String let description: String? let notes: [String] let signature: [String: [String]?] let frequency: CountableClosedRange<Int> }
let cMajor =Chord(name: "C", description: "C major", notes: ["C", "E", "G"], signature: ["sharp": nil, "flat": nil], frequency: 432...446) let aMinor =Chord(name: "Am", description: "A minor", notes: ["A", "C", "E"], signature: ["sharp": nil, "flat": nil], frequency: 440...446) let chords: Set= [cMajor, aMinor] let versions = [cMajor: "major", aMinor: "minor"]
class Country: Hashable { let name: String let capital: String init(name: String, capital: String) { self.name = name self.capital = capital } static func ==(lhs: Country, rhs: Country) -> Bool { return lhs.name == rhs.name && lhs.capital == rhs.capital } var hashValue: Int { return name.hashValue ^ capital.hashValue &* 16777619 } }
let france = Country(name: "France", capital: "Paris") let germany = Country(name: "Germany", capital: "Berlin") let countries: Set = [france, germany] let countryGreetings = [france: "Bonjour", germany: "Guten Tag"]