升级 Xcode 15 之后,你能用到哪些 swift 新特性?

这里每天分享一个 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 宏,我在之前的文章有单独详细介绍过,感兴趣的可以去看看。

完整介绍一下关于 Xcode 15 新出的宏

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 方法

这个变化引入用于创建 AsyncStreamAsyncThrowingStream 实例的辅助方法,这使得流的延续性更容易访问。

使用方法:

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 中已经有 KVOObservableObject,但 KVO 只能与 NSObject 一起使用,而 ObservableObject 需要使用 Combine

新增的 Observation 适用于所有 Swift 引用类型,而不仅仅是继承自 NSObject 的引用类型,并且能够利用语言的优势跨平台工作诸如 async / await 之类的功能。

使用 @Observation 宏标记类,之后,这个类所有的存储属性都可被监听

kotlin 复制代码
@Observable class Car {
    var name: String
}

15、使 Never 符合 Codable 协议

标准库向 Never 类型遵守 EncodableDecodable 协议。

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新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
mascon33 分钟前
U3D打包IOS的自我总结
ios
名字不要太长 像我这样就好39 分钟前
【iOS】继承链
macos·ios·cocoa
karshey2 小时前
【IOS webview】IOS13不支持svelte 样式嵌套
ios
潜龙95272 小时前
第4.3节 iOS App生成追溯关系
macos·ios·cocoa
游戏开发爱好者811 小时前
iOS App 电池消耗管理与优化 提升用户体验的完整指南
android·ios·小程序·https·uni-app·iphone·webview
程序员老刘16 小时前
AI智能体正在颠覆App开发,不转型就淘汰
flutter·客户端·mcp
神策技术社区17 小时前
iOS 全埋点点击事件采集白皮书
大数据·ios·app
wuyoula18 小时前
iOS V2签名网站系统源码/IPA在线签名/全开源版本/亲测
ios
2501_9159184119 小时前
iOS 性能监控工具全解析 选择合适的调试方案提升 App 性能
android·ios·小程序·https·uni-app·iphone·webview
fishycx19 小时前
iOS 构建配置与 AdHoc 打包说明
ios