WWDC Quick Look 💓 By SwiftGGTeam
What's new in Swift

What's new in Swift

元の動画を見る

ハイライト

Swift 5.9 では、if/switch 式、パラメーター パック (パラメーター パック)、Swift マクロ、@Observable マクロ、~Copyable 所有権制御、C++ 相互運用機能、カスタム アクター エグゼキューターなどの機能が導入され、コード式がより簡潔になり、汎用 API がより柔軟になり、コンパイル時に生成されるコードがより安全になると同時に、Swift のパフォーマンスとセキュリティの利点が C++ とサーバー側分散プログラムの混合コード ベースに拡張されます。

主な内容

if/switch 式の割り当ての簡略化

以前は条件に基づいて寄付する必要がありましたlet変数の割り当ては、ネストされた三項演算子または即時クロージャーのハックを使用してのみ実行できます。コードは醜く、エラーが発生しやすくなります。

03:06

Swift 5.9 では、if/else および switch を式として直接使用できます。使い慣れた条件ステートメントを使用して、変数、プロパティ、さらには return ステートメントを初期化します。

03:46

パラメータ パッケージの最終リロード爆発

さまざまな数のパラメーターをサポートするために、関数に 6 つのオーバーロードされたバージョンがあるコードを書いたことがありますか?各バージョンのロジックは、パラメーターの数を除いてまったく同じです。さらに悪いことに、7 番目のパラメータによりコンパイル エラーが発生します。

06:03

パラメーター バッグを使用すると、各パラメーターの静的な型情報を保持しながら、任意の数の型パラメーターを処理できるジェネリック関数を作成できます。呼び出し方法はオーバーロードを使用する場合とまったく同じです。

07:36

Swift マクロはコンパイル時にコードを生成します

マクロは Swift 5.9 の最も重要な新機能です。これにより、コンパイル時にコードに基づいて新しいコードを生成できるようになり、型の安全性を維持しながら定型コードを削減できます。

10:01

例えばassert(a == b)失敗した場合、通常のアサーションでは「アサーションが失敗しました」と通知されるだけで、a と b の具体的な値はわかりません。マクロとして実装されたアサーションは、各部分式の値を取得し、失敗時に「a は 10、b は 17」を通知するコードに拡張できます。

11:02

@Observableマクロは、Apple 自体がフレームワークで使用しているものの例です。本来は手動で書く必要があったものを必要とするObservableObject + @Published + objectWillChange.send()すべて自動化され、より詳細な依存関係の追跡が可能になります。

16:57

~コピー可能なコントロールの値の型のコピー

Swift の値型はデフォルトで任意にコピーできます。ほとんどの場合、これは良いことですが、ファイル記述子、ネットワーク接続などのリソースを扱う場合、コピーによって重複解放やリソース リークが発生する可能性があります。

23:59

~Copyable値の型がコピー不可能であることを宣言できます。協力するconsumingこの方法では、コンパイラは、ランタイムがクラッシュするまで待つのではなく、コンパイル時に「解放されたリソースの使用」エラーを防ぐことができます。

26:06

C++ 相互運用性

Swift 5.9 は C++ との双方向相互運用機能を実装しています。Swift では、C++ クラスのメソッドを直接呼び出したり、プロパティにアクセスしたり、STL コンテナを使用したりできます。 C++ で Swift 関数を呼び出し、Swift 構造を使用することもできます。

28:52

これは、大規模な C++ コード ベースを 1 回だけ書き直すことなく、段階的に Swift に導入できることを意味します。

カスタム アクター エグゼキュータ

アクターのデフォルトのエグゼキューターは、システム管理のシリアル キューです。しかし、アクターを特定のディスパッチ キュー (SQLite のシリアル キューなど) にバインドする必要がある場合、以前はその方法がありませんでした。

35:30

Swift 5.9 では、Actor エグゼキュータをカスタマイズできます。DispatchSerialQueueすでに組み込まれていますSerialExecutorプロトコルのサポートについては、アクター内で宣言するだけで済みます。unownedExecutorそれでおしまい。

35:58

##詳細

if 式は三項演算子を置き換えます

// 以前:嵌套三元运算符难以阅读
let bullet =
    isRoot && (count == 0 || !willExpand) ? ""
        : count == 0    ? "- "
        : maxDepth <= 0 ? "▹ " : "▿ "

03:06

// Swift 5.9:用 if 表达式
let bullet =
    if isRoot && (count == 0 || !willExpand) { "" }
    else if count == 0 { "- " }
    else if maxDepth <= 0 { "▹ " }
    else { "▿ " }

03:19

// 以前:属性初始化需要闭包 hack
let attributedName = AttributedString(markdown: displayName)

03:30

// Swift 5.9:直接用 if 表达式初始化
let attributedName =
    if let displayName, !displayName.isEmpty {
        AttributedString(markdown: displayName)
    } else {
        "Untitled"
    }

03:46

キーポイント:

  • if/else と switch は、式が期待される場所であればどこでも使用できるようになりました
  • 各ブランチは同じタイプの値を返さなければなりません
  • 変数の初期化、属性のデフォルト値、関数の戻りなどのシナリオに特に適しています。
  • 三項演算子のネストよりもはるかに読みやすい

パラメーター パックの代替オーバーロード

// 以前:为每个参数数量写重载
func evaluate<Result>(_ request: Request<Result>) -> Result
func evaluate<R1, R2>(_ r1: Request<R1>, _ r2: Request<R2>) -> (R1, R2)
func evaluate<R1, R2, R3>(_ r1: Request<R1>, _ r2: Request<R2>, _ r3: Request<R3>) -> (R1, R2, R3)
// ... 写到 6 个参数就停了

// 第 7 个参数编译报错:Extra argument in call
let results = evaluator.evaluate(r1, r2, r3, r4, r5, r6, r7)

06:35

// Swift 5.9:一个函数处理任意数量
func evaluate<each Result>(_: repeat Request<each Result>) -> (repeat each Result)

07:36

// 调用方式和以前一样
let value = RequestEvaluator().evaluate(request)
let (x, y) = RequestEvaluator().evaluate(r1, r2)
let (x, y, z) = RequestEvaluator().evaluate(r1, r2, r3)

08:21

キーポイント:

  • each Result型パラメータパックを宣言する
  • repeat Request<each Result>任意の数の引数に展開します
  • repeat each Result戻り値の型の対応する数のタプル要素に展開します。
  • 呼び出し構文はオーバーロードとまったく同じであり、ユーザーはそれを認識しません
  • 「最大 N パラメータ」という人為的な制限はもうありません

Swift マクロの基本的な使用法

// 导入宏包
import PowerAssert

// 使用宏:失败时显示每个子表达式的值
#assert(max(a, b) == c)
// 输出:Assertion failed: max(a, b) == c
//        |   |  |  |  |
//        |   10 17 10 17
//        false

11:02

// 宏声明
@freestanding(expression)
public macro assert(_ condition: Bool)

// 宏定义:指向编译器插件
public macro assert(_ condition: Bool) = #externalMacro(
    module: "PowerAssertPlugin",
    type: "PowerAssertMacro"
)

12:07

// 宏展开后的代码
PowerAssert.Assertion("#assert(a == b)") {
    $0.capture(a, column: 8) == $0.capture(b, column: 13)
}

13:14

キーポイント:

  • ホンイ#初めに、例えば#assert#Predicate
  • @freestanding(expression)スタンドアロンの式マクロを表します
  • マクロパラメータは、通常の関数と同様に型チェックされます。
  • マクロはコンパイル時に展開され、通常の Swift コードが生成されます。
  • Xcode は、生成されたコードを表示するためのマクロの展開をサポートし、マクロによって生成されたコード内のブレークポイントの設定もサポートします。

@Observable マクロによりデータの観察が簡素化されます

// 以前:ObservableObject + @Published
final class Person: ObservableObject {
    @Published var name: String
    @Published var age: Int
    @Published var isFavorite: Bool
}

struct ContentView: View {
    @ObservedObject var person: Person

    var body: some View {
        Text("Hello, \(person.name)")
    }
}

16:57

// Swift 5.9:@Observable 宏
@Observable final class Person {
    var name: String
    var age: Int
    var isFavorite: Bool
}

struct ContentView: View {
    var person: Person

    var body: some View {
        Text("Hello, \(person.name)")
    }
}

17:25

キーポイント:

  • @Observableはい@attached3 つの役割を含むマクロ: member、memberAttribute、および準拠
  • 各属性の観測追跡を自動的に追加します。手動で記述する必要はありません@Published
  • ビューでは不要になりました@ObservedObject、属性を直接引用するだけです
  • 実際に読み取られた属性のみを更新、粒度比objectWillChangeより細かい

~コピー可能なコントロールのリソースのライフサイクル

// 问题:struct 可以随意复制,导致文件描述符重复关闭
struct FileDescriptor {
    private var fd: CInt

    init(descriptor: CInt) { self.fd = descriptor }

    func close() {
        Darwin.close(fd)
    }
}

// 修复:标记为不可复制
struct FileDescriptor: ~Copyable {
    private var fd: CInt

    init(descriptor: CInt) { self.fd = descriptor }

    func write(buffer: [UInt8]) throws { /* ... */ }

    consuming func close() {
        Darwin.close(fd)
    }

    deinit {
        Darwin.close(fd)
    }
}

26:06

// 编译器会在编译期阻止错误用法
let file = FileDescriptor(fd: descriptor)
file.close() // consuming 调用,file 的生命周期结束
file.write(buffer: data) // 编译错误:'file' used after consuming

27:20

キーポイント:

  • ~Copyable値の型が暗黙的にコピーできないことを示します
  • consuming funcこのメソッドを呼び出すと値の所有権が消費されることを示します
  • コンパイラは値のライフサイクルを追跡し、コンパイル時の消費後の使用を防ぎます。
  • ファイル記述子、ネットワーク接続、ロックなどのコピー不可能なリソースの管理に適しています。

C++ 相互運用性

// C++ 头文件 Person.h
// struct Person {
//     std::string name;
//     unsigned getAge() const;
// };
// std::vector<Person> everyone();

// Swift 中直接调用 C++ API
func greetAdults() {
    for person in everyone().filter { $0.getAge() >= 18 } {
        print("Hello, \(person.name)!")
    }
}

28:52

// Swift 代码
struct LabeledPoint {
    var x = 0.0, y = 0.0
    var label: String = "origin"
    mutating func moveBy(x deltaX: Double, y deltaY: Double) { /* ... */ }
    var magnitude: Double { /* ... */ }
}

// C++ 中调用 Swift
#include <Geometry-Swift.h>

void test() {
    Point origin = Point()
    Point unit = Point::init(1.0, 1.0, "unit")
    unit.moveBy(2, -2)
    std::cout << unit.label << " moved to " << unit.magnitude() << std::endl
}

29:51

キーポイント:

  • Swift は C++ クラス、メソッド、プロパティ、STL コンテナを直接使用できます。
  • C++ は、生成されたヘッダー ファイルを通じて Swift の構造とメソッドを呼び出すことができます
  • 値セマンティクスと参照セマンティクスのマッピングをサポート
  • C++ コードベースを Swift に段階的に移行するのに最適

カスタム アクター エグゼキュータ

// 默认 Actor:使用系统管理的串行队列
actor MyConnection {
    private var database: UnsafeMutablePointer<sqlite3>

    init(filename: String) throws { /* ... */ }
    func pruneOldEntries() { /* ... */ }
}

// 自定义 Actor:绑定到指定的 DispatchQueue
actor MyConnection {
    private var database: UnsafeMutablePointer<sqlite3>
    private let queue: DispatchSerialQueue

    nonisolated var unownedExecutor: UnownedSerialExecutor {
        queue.asUnownedSerialExecutor()
    }

    init(filename: String, queue: DispatchSerialQueue) throws { /* ... */ }
    func pruneOldEntries() { /* ... */ }
}

35:30

キーポイント:

  • nonisolated var unownedExecutorアクターが使用するエグゼキュータを指定します
  • DispatchSerialQueue組み込みのサポートSerialExecutorプロトコル
  • エグゼキューターをカスタマイズした後、アクターのすべてのメソッドが指定されたキューで実行されます
  • SQLite、Core Dataなど、特定のスレッド/キューにバインドする必要があるシナリオに適しています。

重要ポイント

1.プロジェクト内のネストされた三項演算子を if/switch 式に置き換えます

コードベース内の複数レベルのネストを検索します?:演算子、特に変数初期化シナリオの場合。Swift 5.9 の if 式により、コードの意図がより明確になります。入り口はXcodeの検索機能、search?そして:入れ子のパターン。

2.新しいプロジェクトでは、ObservableObject の代わりに @Observable を直接使用します

新しいプロジェクトのデータ モデルを直接使用できます。@Observable class Model、ビューでは不要になりました@ObservedObject。依存関係の追跡はより正確になり、コードはよりシンプルになります。入り口は、import Observation、消去@PublishedそしてObservableObjectプロトコル。

3.パラメーター パッケージを検討して汎用 API を簡素化します

「異なる数の引数に対してオーバーロードされた」API がある場合は、パラメーター パックを使用してそれを関数にリファクタリングします。ユーザーの呼び出し方法は変更されず、保守コストが大幅に削減されます。入り口は学びですeachそしてrepeatキーワード構文。

4.マクロを使用してプロジェクト内の定型コードを排除

Equatable の実装、Codable のカスタム キー マッピング、列挙型の大文字と小文字の検出プロパティなど、繰り返し発生するコード パターンについてプロジェクトを調べます。これらはすべてマクロを使用して自動化できます。エントリ ポイントは、Swift パッケージ タイプのマクロ プロジェクトを作成することです。を参照してください。@CaseDetection実現。

5.基礎となるリソース管理タイプに ~Copyable を追加します

プロジェクトにファイル記述子、ネットワーク ハンドル、GPU リソースを管理する構造がある場合は、それらを次のようにマークすることを検討してください。~Copyableそして追加しますconsuming方法。コンパイラは、コンパイル時にリソース使用量エラーを検出します。資源包装タイプに入口を追加: ~Copyable

関連セッション

コメント

GitHub Issues · utterances