apple / swift-evolution
1、简化的if let/guard let语法
SE-0345 引入了新的速记语法,可以将可选型展开为同名的变量。以后我们可以像下面这样解包了:
let name: String? = "zhangsan"
print(name) // Optional("zhangsan")
// Swift5.7之前
// if let
if let name = name {
print(name) // zhangsan
}
// Swift5.7之后
// if let
if let name {
print(name) // zhangsan
}
func method(name: String?, age: Int?) {
// Swift5.7之前
// guard let
guard let name = name else { return }
// Swift5.7之后
// guard let
guard let age else { return }
print("姓名\(name),年龄\(age)")
}
2 、增强的闭包类型推断
SE-0326 极大地提高了 Swift 对闭包使用参数和类型推断的能力,这意味着我们现在可以删除许多必须明确指定输入和输出类型的写法。
let array: [String] = ["11", "2", "3", "40", "5"]
// Swift5.7之前
let newArray1 = array.map { str -> Int in
if str.count > 1 {
return Int(str) ?? 0
} else {
return Int(10)
}
}
print(newArray1) // [11, 10, 10, 40, 10]
// Swift5.7之后
let newArray2 = array.map { str in
if str.count > 1 {
return Int(str) ?? 0
} else {
return Int(10)
}
}
print(newArray2) // [11, 10, 10, 40, 10]
3、基于默认表达式的类型推断
SE-0347 扩展了 Swift 使用泛型参数类型的默认值的能力。如果你有一个泛型类型或函数,现在可以为默认表达式提供一个具体类型。
例如,我们可能有一个函数,它从任意类型的序列中返回 count
个随机项:
func drawLotto1(from options: T, count: Int = 7) -> [T.Element] {
Array(options.shuffled().prefix(count))
}
这允许我们使用任何类型的序列来运行函数,例如字符串数组或者整数范围:
print(drawLotto1(from: 1...49))
print(drawLotto1(from: ["Jenny", "Trixie", "Cynthia"], count: 2))
SE-0347
允许我们为函数中的 T
参数提供一个具体类型作为默认值,同时允许我们保持使用字符串数组或任何其他序列类型的灵活性:
func drawLotto2(from options: T = 1...49, count: Int = 7) -> [T.Element] {
Array(options.shuffled().prefix(count))
}
这样一来我们既可以使用自定义序列调用函数,也可以让默认值接管:
print(drawLotto2(from: ["Jenny", "Trixie", "Cynthia"], count: 2))
print(drawLotto2())
4、 函数参数与返回类型支持不透明结果类型
SE-0341 解锁了在使用更简单泛型的地方对参数声明使用 some
的能力。
some
--- 用在当返回值为不确定类型的情况。
some
类型只对已声明的属性和下标类型以及函数的返回类型实现
举个例子,如果我们想编写一个检查数组是否排序的函数,Swift 5.7 及更高版本允许我们这样写:
func isSorted(array: [some Comparable]) -> Bool {
array == array.sorted()
}
[some Comparable]
参数类型意味着此函数适用于包含某种类型的元素的数组,该类型遵循 Comparable
协议,这是等效通用代码的语法糖:
func isSortedOld(array: [T]) -> Bool {
array == array.sorted()
}
当然,我们也可以写更长的约束扩展:
extension Array where Element: Comparable {
func isSorted() -> Bool {
self == self.sorted()
}
}
这种简化的泛型语法确实意味着我们不再有能力为我们的类型添加更复杂的约束,因为合成的泛型参数没有特定的名称。
5、结构化的不透明结果类型
SE-0328 拓宽了不透明结果类型可以使用的范围。
例如,我们现在可以一次返回多个不透明类型:
func showUserDetails() -> (some Equatable, some Equatable) {
("Username", "@twostraws")
}
返回不透明类型数组:
func createUser() -> [some Equatable] {
["Username", "Username"]
}
6、 不可异步属性
SE-0340 通过允许我们将类型和函数标记为在异步上下文中不可用,部分屏蔽了 Swift 并发模型中的潜在风险情况。
要将某些内容标记为在异步上下文中不可用,需要使用 @available
,然后在末尾添加 noasync
,例如:
@available(*, noasync)
func doRiskyWork() {
}
在常规的同步函数中,我们可以正常调用它:
func synchronousCaller() {
doRiskyWork()
}
假如我们试图在异步函数中也这么做,Swift 会指出错误:
func asynchronousCaller() async {
doRiskyWork()
}
这个保护机制是一种改进,但我们不应过度依赖它。因为它无法防范我们把调用嵌套到同步函数内部的情况,例如:
func sneakyCaller() async {
synchronousCaller()
}
上面的代码在异步上下文中运行,但调用同步函数,该函数又可以调用 noasync
函数 doRiskyWork()
。
7、正则表达式
- SE-0350 引入了新的
Regex
类型 - SE-0351 引入了一个用于创建正则表达式的
result builder
驱动的 DSL。 - SE-0354 引入了使用
/.../
而不单是Regex
来共同创建正则表达式的方式。 - SE-0357 添加了许多新的基于正则表达式的字符串处理算法。
现在,让我们从简单的例子开始:
let message = "the cat sat on the mat"
print(message.ranges(of: "at"))
print(message.replacing("cat", with: "dog"))
print(message.trimmingPrefix("the "))
//寻找“The”,但将正则表达式修改为不区分大小写,以便匹配“the”、“THE”等。
print(message.trimmingPrefix(/The/.ignoresCase()))
Swift 还提供了专门的 Regex 类型:
do {
let atSearch = try Regex("[a-z]at")
print(message.ranges(of: atSearch))
} catch {
print("Failed to create regex")
}
这里两种方式有一个关键区别:当我们使用 Regex 从字符串创建正则表达式时,Swift 必须在运行时解析字符串以找出它应该使用的实际表达式。相比之下,使用正则表达式字面量允许 Swift 在编译时 检查你的正则表达式:它可以验证正则表达式不包含错误,并且还可以准确了解它将包含什么匹配项。
func regularFunc() {
let message = "This is a regex test case 123456789 一二三"
do {
// 字符串中搜索regex
let regex = try Regex("[a-z] regex")
let index = message.ranges(of: regex)
// 范围
print("范围--\(index)")
// 替换
print(message.replacing(regex, with: "正则表达式"))
} catch {
print("Failed to create Regex")
}
// /.../创建正则表达式,查找数字并替换
// \d+:一个或多个数字。
print(message.replacing(/(\d+)/, with: "我是数字"))
}
相关文章
1、Xcode14新特性(基于Xcode 14 Beta1)
2、史上最全正则表达式语法,文末附常用表达式!
3、Swift5.7下