WWDC 2022 总结
又是一年一度的 WWDC,例行总结一下。
1. Swift 5.7 更新内容
1.1 if let
简化
当一个变量名非常长时,if let
只能像这样:
var thisIsALongNameOfParameter: String? = nil
if let thisIsALongNameOfParameter = thisIsALongNameOfParameter {
print(thisIsALongNameOfParameter)
}
上面的看起来很啰嗦,有时为了简化,会取一个短的名字
var thisIsALongNameOfParameter: String? = nil
if let name = thisIsALongNameOfParameter {
print(name)
}
Swift 5.7 之后,可以缩短成if let
+ 变量名即可,guard let
也是类似的。
var thisIsALongNameOfParameter: String? = nil
if let thisIsALongNameOfParameter {
print(thisIsALongNameOfParameter)
}
但这个简写功能只能用于对象本身,不能用于对象的属性。
下面这种就会报错:
struct Book {
let price: Float?
}
let book: Book = Book(price: 32.0)
if let book.price {
print(book.price ?? 0.0)
}
1.2 闭包的类型推断
Swift 5.7 之前,闭包的类型推断只能用于单个表达式,比如下面的results
可以通过编译器的类型推断出是[String]
类型:
var numbers = [4, 3, 2, 1]
let results = numbers.map {
"\($0)"
}
但当里面有多个表达式的时候,比如if else
判断或do catch
等条件表达式时,编译器就会报错误
let results = numbers.map {
if $0.isMultiple(of: 2) {
return "\($0)"
} else {
return "\($0 * 2)"
}
}
// error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate
// let results = numbers.map {
解决办法也很简单,指定闭包的返回值即可:
let results = numbers.map { n -> String in
if n.isMultiple(of: 2) {
return "\(n)"
} else {
return "\(n * 2)"
}
}
Swift 5.7 对上面这种情况做了简化处理,编译器可以推断出多个表达式的类型,应该是推断return
的类型,无需手动指定返回类型了。
但这个功能的前提条件是所有的return
类型都是同一类型,不能return
不同的类型,如下面的情况会报编译器错误。
let results = numbers.map { n in
if n.isMultiple(of: 2) {
return "\(n)"
} else {
return n * 2
}
}
1.3 从内存安全到线程安全
内存安全
在 Swift 5.7 之前,我们在做数组删除操作时,又去访问数组的长度,编译器会报错,这个叫内存安全。
var numbers = [4, 3, 2, 1]
numbers.removeAll(where: { number in
number == numbers.count
})
编译器报错
Overlapping accesses to 'numbers', but modification requires exclusive access; consider copying to a local variable
线程安全
Swift 5.7 中,当我们在Task
里面操作数组时一样,编译器同样会报警告:
var numbers = [3, 2, 1]
Task {
numbers.append(0)
}
numbers.removeLast()
print(numbers)
编译器警告
Mutation of captured var 'numbers' in concurrently-executing code; this is an error in Swift 6
1.4 Swift Regex
我一直认为正则表达式的可读性非常差,所以 Apple 在 Objective-C 时代推出了NSPredicate。但是 Swift 时代,NSPredicate 这种偏重字符串格式化的方法不符合强类型的思想,所以 Regex 诞生了。我认为这是可以预见的结果,通过函数式的方式组合各种条件达到非常复杂的正则效果。
看下面的例子:
import RegexBuilder
let regex = Regex {
ZeroOrMore(.horizontalWhitespace)
Optionally {
Capture(OneOrMore(.noneOf("<#")))
}
.repetitionBehavior(.reluctant)
ZeroOrMore(.horizontalWhitespace)
"<"
Capture(OneOrMore(.noneOf(">#")))
">"
ZeroOrMore(.horizontalWhitespace)
ChoiceOf {
"#"
Anchor.endOfSubjectBeforeNewline
}
}
非常的语义化,一眼就看的出来是什么意思。
唯一的缺点就是只能在 iOS 16 上使用,遗憾。
1.5 时间标准
Swift 5.7 引入了一种新的标准方式来获取和表示时间,可分为以下三个部分:
- Clock: 表示当下,并且能提供在将来特定时间点唤起的功能
- Instant: 表示某个瞬间
- Duration: 用于计量流逝的时间
Clock 有 ContinuousClock 和 SuspendingClock 两种内置时钟,ContinuousClock 在系统休眠时也会保持时间递增,而 SuspendingClock 则不会。Task 休眠相关的 API 也会根据新标准有所更新。
extension Task {
@available(*, deprecated, renamed: "Task.sleep(for:)")
public static func sleep(_ duration: UInt64) async
@available(*, deprecated, renamed: "Task.sleep(for:)")
public static func sleep(nanoseconds duration: UInt64) async throws
public static func sleep(for: Duration) async throws
public static func sleep<C: Clock>(until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, clock: C) async throws
}
2. UIKit 16 更新内容
2.1 改进的导航栏
Navigation Bar 现在有 3 种类型:
- Navigator: iOS 16 之前的样式
- Browser: 浏览器样式,可以用于支持"History"之类的 web 浏览器功能,或者侧边目录结构的文档浏览器。
- Editor: 编辑器样式支持在中间点击的时候会弹窗,然后拖拽功能按钮到导航栏,类似于 macOS 上的应用的自定义工具栏。
2.2 标题菜单
可在新的导航栏设置标题菜单,可处理复制、移动、重命名、打印等操作。