这里每天分享一个 iOS 的新知识,快来关注我吧
前言
Xcode 15 正式版已经发布了,其中内置了 swift 5.9 版本,今天来罗列一下 swift 5.9 有哪些新特性。
1、新增 consume 运算符
consume
运算符可以结束局部变量的生命周期,举个例子:
scss
let arr = [1, 2, 3]
consume arr
print(arr)
arr 被 consume 标记之后,明确告诉编译器,这个值在之后的代码中不允许再次使用,因此在第三行 print 的时候会报错:
'arr' used after consume
2、将 sleep(for:) 方法添加到 Clock 协议中
swift
let clock: any Clock<Duration> = ContinuousClock()
try await clock.sleep(for: .seconds(2), tolerance: .seconds(5))
print("睡眠两秒之后执行")
3、增加参数所有权关键字 borrowing 和 consuming
borrowing 和 consuming 是主要是为不可复制类型提供的两个新属性。
swift
func consume(_ number: consuming SomeType) {}
func borrow(_ number: borrowing SomeType) {}
调用被标记 consuming
的方法参数会被消费掉,有点像上边提到的 consume
关键字。
另一个属性 borrowing
表示该参数只是以只读模式借用外部值。可以简单的将其视为一个引用指针。
4、if 和 switch 表达式
这个在之前的文章中有详细介绍过:Swift 5.9 新增 if 和 switch 作为表达式的能力
允许下边这两种写法:
bash
let x = if p { 0 } else { 1.0 }
csharp
let y: Float = switch x.value {
case 0..<0x80: 1
case 0x80..<0x0800: 2.0
case 0x0800..<0x1_0000: 3.0
default: 4.5
}
5、新型的结构化并发任务组:DiscardingTaskGroup
DiscardingTaskGroup
将立即丢弃其子任务的结果并释放产生该结果的子任务。这允许高效且"永远运行"的请求接受循环,例如 HTTP 或 RPC 服务器。
csharp
try await withThrowingDiscardingTaskGroup() { group in
while let newConnection = try await listeningSocket.accept() {
group.addTask {
handleConnection(newConnection)
}
}
}
这与 iOS 13 出的 withThrowingTaskGroup
函数相比,不会泄漏任务,因此是安全的,是表达此类处理程序循环的推荐方法。
6、今年的重头戏 Swift Macros
关于 Swift 宏,我在之前的文章有单独详细介绍过,感兴趣的可以去看看。
7、导入前向声明的 Objective-C 接口和协议
使用方法:
less
// @class Foo turns into
@available(*, unavailable, message: "This Objective-C class has only been forward declared; import its owning module to use it")
class Foo : NSObject {}
// @protocol Bar turns into
@available(*, unavailable, message: "This Objective-C protocol has only been forward declared; import its owning module to use it")
protocol Bar : NSObjectProtocol {}
8、新的访问修饰符 package
目前,要访问另一个模块中的符号,需要声明该符号为 public
。然而,符号 public
允许从任何模块访问它,无论是在包内还是从包外,这有时是不可取的。
被 package
修饰符标记的符号只能被包内的模块访问。
这在之前的文章中也有介绍过:swift 5.9 新增访问修饰符。
9、AsyncStream 和 AsyncThrowingStream 上添加 makeStream 方法
这个变化引入用于创建 AsyncStream
和 AsyncThrowingStream
实例的辅助方法,这使得流的延续性更容易访问。
使用方法:
scss
let (stream, continuation) = AsyncStream.makeStream(of: Int.self)
await withTaskGroup(of: Void.self) { group in
group.addTask {
for i in 0...9 {
continuation.yield(i)
}
continuation.finish()
}
group.addTask {
for await i in stream {
print(i)
}
}
}
10、不可复制的结构体和枚举
默认情况下,我们创建的结构体和枚举都是可复制的,这意味着可以为该类型的任何值创建多个相同的、可互换的表示形式。然而,有时候我们需要不可复制的类型。
可以使用 ~Copyable
来约束结构体或枚举是否可以被复制:
arduino
struct MyStruct: ~Copyable {
}
enum MyEnum: ~Copyable {
}
如果尝试复制,将会报错:
ini
let mys = MyStruct()
let mys1 = mys // Error: Cannot consume noncopyable stored property 'mys' that is global
11、自定义 Actor 执行者
使用方法:
swift
class SpecificThreadExecutor: SerialExecutor {
let someThread: SomeThread // simplified handle to some specific thread
func enqueue(_ job: consuming ExecutorJob) {
let unownedJob = UnownedExecutorJob(job) // in order to escape it to the run{} closure
someThread.run {
unownedJob.runSynchronously(on: self)
}
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
}
extension SpecificThreadExecutor {
static var sharedUnownedExecutor: UnownedSerialExecutor {
// ... use some shared configured instance and return it ...
}
}
actor Worker {
nonisolated var unownedExecutor: UnownedSerialExecutor {
// use the shared specific thread executor mentioned above.
// alternatively, we can pass specific executors to this actors init() and store and use them this way.
SpecificThreadExecutor.sharedUnownedExecutor
}
}
12、值和类型参数包
泛型函数当前需要固定数量的类型参数。不可能编写无限数量具有不同类型的参数的泛型函数。
swift 5.9 添加的值和类型参数包,允许这么做了:
swift
func test<each T>(_ p: repeat each T) {
}
// 可以传入无限个范型参数
test(1, "1", 1.0, [1, 2, 3])
13、swift Macro 支持 SPM
第三方提供的宏,可以通过 SPM 来引入项目。
14、Observation
Swift 中已经有 KVO
和 ObservableObject
,但 KVO
只能与 NSObject
一起使用,而 ObservableObject
需要使用 Combine
。
新增的 Observation
适用于所有 Swift 引用类型,而不仅仅是继承自 NSObject
的引用类型,并且能够利用语言的优势跨平台工作诸如 async
/ await
之类的功能。
使用 @Observation
宏标记类,之后,这个类所有的存储属性都可被监听
kotlin
@Observable class Car {
var name: String
}
15、使 Never 符合 Codable 协议
标准库向 Never
类型遵守 Encodable
和 Decodable
协议。
16、允许通过包抽象泛型
这个改变和前面提到的第 12 条相关,在泛型类型的泛型参数列表中, each
关键字声明泛型参数包,就像在泛型函数的泛型参数列表中一样。存储属性的类型可以包含包扩展类型。
使用方法:
swift
struct ZipSequence<each S: Sequence>: Sequence {
typealias Element = (repeat each S.Element)
let seq: (repeat each S)
func makeIterator() -> Iterator {
return Iterator(iter: (repeat (each seq).makeIterator()))
}
struct Iterator: IteratorProtocol {
typealias Element = (repeat each S.Element)
var iter: (repeat each S.Iterator)
mutating func next() -> Element? {
return ...
}
}
}
17、元组值包的扩展
类型参数包仅允许在函数参数列表、元组元素或泛型参数列表中使用。这阻止将类型参数包声明为函数返回类型、类型别名或局部变量类型以允许存储。针对这些限制的可用解决方案是将值包包含在元组中。
使用方法:
swift
func tuplify<each T>(_ value: repeat each T) -> (repeat each T) {
return (repeat each value)
}
func example<each T>(_ value: repeat each T) {
let abstractTuple = tuplify(repeat each value)
repeat print(each abstractTuple) // okay as of this proposal
let concreteTuple = (true, "two", 3)
repeat print(each concreteTuple) // invalid
let mixedConcreteAndAbstractTuple = (1, repeat each value)
repeat print(each mixedConcreteAndAbstractTuple) // invalid
let labeledAbstractTuple = (label: repeat each value)
repeat print(each labeledAbstractTuple) // invalid
}
18、初始化访问器
支持在计算属性中访问初始化方法
swift
struct Angle {
var degrees: Double
var radians: Double {
@storageRestrictions(initializes: degrees)
init(initialValue) {
degrees = initialValue * 180 / .pi
}
get { degrees * .pi / 180 }
set { degrees = newValue * 180 / .pi }
}
init(degrees: Double) {
self.degrees = degrees // initializes 'self.degrees' directly
}
init(radiansParam: Double) {
self.radians = radiansParam // calls init accessor for 'self.radians', passing 'radiansParam' as the argument
}
}
这里每天分享一个 iOS 的新知识,快来关注我吧
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!