内容简介:The first beta of Swift 5.2 just landed with Xcode 11.4 beta, and it includes a handful of language changes alongside reductions in code size and memory usage, plus a new diagnostic architecture architecture that will help you diagnose errors faster.In thi
The first beta of Swift 5.2 just landed with Xcode 11.4 beta, and it includes a handful of language changes alongside reductions in code size and memory usage, plus a new diagnostic architecture architecture that will help you diagnose errors faster.
In this article I'm going to walk through what's changed with some practical examples so you can see for yourself how things have evolved. I encourage you to follow the links through to the Swift Evolution proposals for more information, and if you missed my earlier what's new in Swift 5.1 article then check that out too.
- Tip: You can download this article as an Xcode playground: https://github.com/twostraws/whats-new-in-swift-5-2
Key Path Expressions as Functions
SE-0249 introduced a marvelous shortcut that allows us to use keypaths in a handful of specific circumstances.
The Evolution proposal describes this as being able to use “ \Root.value
wherever functions of (Root) -> Value
are allowed”, but what it means is that if previously you sent a Car into a method and got back its license plate, you can now use Car.licensePlate
instead.
This is best understood as an example, so here’s a User
type that defines four properties:
struct User { let name: String let age: Int let bestFriend: String? var canVote: Bool { age >= 18 } }
We could create some instance of that struct and put them into an array, like this:
let eric = User(name: "Eric Effiong", age: 18, bestFriend: "Otis Milburn") let maeve = User(name: "Maeve Wiley", age: 19, bestFriend: nil) let otis = User(name: "Otis Milburn", age: 17, bestFriend: "Eric Effiong") let users = [eric, maeve, otis]
Now for the important part: if you want to get an array of all the users names, you can do so by using a key path like this:
let userNames = users.map(\.name) print(userNames)
Previously you would have had to write a closure to retrieve the name by hand, like this:
let oldUserNames = users.map { $0.name }
This same approach works elsewhere – anywhere where previously you would have received a value and passed back one of its properties, you can now use a key path instead. For example, this will return all users who can vote:
let voters = users.filter(\.canVote)
And this will return all users who have a best friend:
let bestFriends = users.compactMap(\.bestFriend)
Callable values of user-defined nominal types
SE-0253
introduced introduces statically callable values to Swift, which is a fancy way of saying that you can now call a value directly if its type implements a method named callAsFunction()
. You don’t need to conform to any special protocol to make this behavior work; you just need to add that method to your type.
For example, we could create a Dice
struct that has properties for lowerBound
and upperBound
, then add callAsFunction
so that every time you call a dice value you get a random roll:
struct Dice { var lowerBound: Int var upperBound: Int func callAsFunction() -> Int { (lowerBound...upperBound).randomElement()! } } let d6 = Dice(lowerBound: 1, upperBound: 6) let roll1 = d6() print(roll1)
That will print a random number from 1 through 6, and it’s identical to just using callAsFunction()
directly. For example, we could call it like this:
let d12 = Dice(lowerBound: 1, upperBound: 12) let roll2 = d12.callAsFunction() print(roll2)
Swift automatically adapts your call sites based on how callAsFunction()
is defined. For example, you can add as many parameters as you want, you can control the return value, and you can even mark methods as mutating
if needed.
For example, this creates a StepCounter
struct that tracks how far someone has walked and reports back whether they reached their target of 10,000 steps:
struct StepCounter { var steps = 0 mutating func callAsFunction(count: Int) -> Bool { steps += count print(steps) return steps > 10_000 } } var steps = StepCounter() let targetReached = steps(count: 10)
For more advanced usage, callAsFunction()
supports both throws
and regrows
, and you can even define multiple callAsFunction()
methods on a single type – Swift will choose the correct one depending on the call site, just like regular overloading.
Subscripts can now declare default arguments
When adding custom subscripts to a type, you can now use default arguments for any of the parameters. For example, if we had a PoliceForce
struct with a custom subscript to read officers from the force, we could add a default
parameter to send back if someone tries to read an index outside of the array’s bounds:
struct PoliceForce { var officers: [String] subscript(index: Int, default default: String = "Missing") -> String { if index >= 0 && index < officers.count { return officers[index] } else { return `default` } } } let force = PoliceForce(officers: ["Amy", "Jake", "Rosa", "Terry"]) print(force[0]) print(force[5])
That will print “Amy” then “Unknown”, with the latter being caused because there is no officer at index 5. Note that you do need to write your parameter labels twice if you want them to be used, because subscripts don’t use parameter labels otherwise.
So, because I use default default
in my subscript, I can use a custom value like this:
print(force[-1, default: "The Vulture"])
New and improved diagnostics
Swift 5.2 introduced a new diagnostic architecture that aims to improves the quality and precision of error messages issued by Xcode when you make a coding error. This is particularly apparent when working with SwiftUI code, where Swift would often produce false positive error messages.
For an example, consider code like this:
struct ContentView: View { @State private var name = 0 var body: some View { VStack { Text("What is your name?") TextField("Name", text: $name) .frame(maxWidth: 300) } } }
That attempts to bind a TextField
view to an integer @State
property, which is invalid. In Swift 5.1 this caused an error for the frame()
modifier saying 'Int' is not convertible to 'CGFloat?’
, but in Swift 5.2 and later this correctly identifies the error is the $name
binding:
Cannot convert value of type 'Binding
You can find out more about the new diagnostic architecture on the Swift.org blog .
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
人工智能+:AI与IA如何重塑未来
[美]韩德尔·琼斯(Handel Jones) [中]张臣雄 / 机械工业出版社 / 2018-10 / 55.00
当深度学习模型引发了全世界对人工智能的再次关注时,人工智能迎来第三次高速增长,人工智能(AI)、增强现实(AR)和虚拟现实(VR)正把人类带向新的“智能增强时代”(IA),我们将在不知不觉中接纳机器智能。 针对人类社会长期存在的众多复杂的动态的难题,人机融合智能将会提供全新的解决方案,谷歌、Facebook、微软、亚马逊、腾讯、阿里巴巴、百度等平台巨头纷纷斥千亿巨资布局人工智能的尖端技术;智......一起来看看 《人工智能+:AI与IA如何重塑未来》 这本书的介绍吧!