<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>四娘</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://kemchenj.github.io/</id>
  <link href="https://kemchenj.github.io/" rel="alternate"/>
  <link href="https://kemchenj.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 四娘</rights>
  <subtitle>Hackable life?</subtitle>
  <title>kemchenj</title>
  <updated>2026-09-02T11:08:19.194Z</updated>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<blockquote><p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md">SE-0302 Sendable and @Sendable closures</a></p></blockquote><ul><li>Proposal: <a href="0302-concurrent-value-and-concurrent-closures.md">SE-0302</a></li><li>Authors: <a href="https://github.com/lattner">Chris Lattner</a>, <a href="https://github.com/douggregor">Doug Gregor</a></li><li>Review Manager: <a href="https://github.com/rjmccall">John McCall</a></li><li>Status: <strong>Accepted (2021-03-16)</strong></li><li>Implementation: <a href="https://github.com/apple/swift/pull/35264">apple&#x2F;swift#35264</a></li><li>Major Contributors: Dave Abrahams, Paul Cantrell, Matthew Johnson, John McCall</li><li>Review: (<a href="https://forums.swift.org/t/se-0302-Sendable-and-concurrent-closures/44919">first review</a>) (<a href="https://forums.swift.org/t/returned-for-revision-se-0302-concurrentvalue-and-concurrent-closures/45251">revision announcement</a>) (<a href="https://forums.swift.org/t/se-0302-second-review-sendable-and-sendable-closures/45253">second review</a>) (<a href="https://forums.swift.org/t/accepted-se-0302-sendable-and-sendable-closures/45786">acceptance</a>)</li></ul><!--## Contents  * [Introduction](#introduction)  * [Motivation](#motivation)     * [<g-emoji class="g-emoji" alias="sparkling_heart" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f496.png">💖</g-emoji> Swift   Value Semantics](#-swift--value-semantics)     * [Value Semantic Composition](#value-semantic-composition)     * [Higher Order Functional Programming](#higher-order-functional-programming)     * [Immutable Classes](#immutable-classes)     * [Internally Synchronized Reference Types](#internally-synchronized-reference-types)     * [“Transferring” Objects Between Concurrency Domains](#transferring-objects-between-concurrency-domains)     * [Deep Copying Classes](#deep-copying-classes)     * [Motivation Conclusion](#motivation-conclusion)  * [Proposed Solution   Detailed Design](#proposed-solution--detailed-design)     * [Marker Protocols](#marker-protocols)     * [Sendable Protocol](#sendable-protocol)        * [Tuple conformance to Sendable](#tuple-conformance-to-sendable)        * [Metatype conformance to Sendable](#metatype-conformance-to-sendable)        * [Sendable conformance checking for structs and enums](#sendable-conformance-checking-for-structs-and-enums)        * [Implicit struct/enum conformance to Sendable](#implicit-structenum-conformance-to-sendable)        * [Sendable conformance checking for classes](#sendable-conformance-checking-for-classes)        * [Actor types](#actor-types)        * [Key path literals](#key-path-literals)     * [New @Sendable attribute for functions](#new-sendable-attribute-for-functions)        * [Inference of @Sendable for Closure Expressions](#inference-of-sendable-for-closure-expressions)     * [Thrown errors](#thrown-errors)     * [Adoption of Sendable by Standard Library Types](#adoption-of-sendable-by-standard-library-types)     * [Support for Imported C / Objective-C APIs](#support-for-imported-c--objective-c-apis)  * [Future Work / Follow-on Projects](#future-work--follow-on-projects)     * [Adaptor Types for Legacy Codebases](#adaptor-types-for-legacy-codebases)     * [Objective-C Framework Support](#objective-c-framework-support)     * [Interaction of Actor self and @Sendable closures](#interaction-of-actor-self-and-sendable-closures)     * [Marker protocols as custom attributes](#marker-protocols-as-custom-attributes)  * [Source Compatibility](#source-compatibility)  * [Effect on API resilience](#effect-on-api-resilience)  * [Alternatives Considered](#alternatives-considered)     * [Exotic Type System Features](#exotic-type-system-features)     * [Support an explicit copy hook](#support-an-explicit-copy-hook)  * [Conclusion](#conclusion)  * [Revision history](#revision-history)--><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--A key goal of the Swift Concurrency effort is to “provide a mechanism for isolating state in concurrent programs to eliminate data races.”  Such a mechanism will be a major progression for widely used programming languages — most of them provide concurrent programming abstractions in a way that subjects programmers to a wide range of bugs, including race conditions, deadlocks and other problems.--><p>Swift Concurrency 的其中一个关键目标就是“提供一种机制来隔离并发程序中的状态，以消除 data races”。这样的机制将会是通用编程语言的一次重大进步 – 大多数语言提供的并发编程抽象会使程序员面临范围宽广的 bug，包括 race conditions，死锁和其它问题。</p><!--This proposal describes an approach to address one of the challenging problems in this space — how to type check value passing between structured concurrency constructs and actors messages. As such, this is a unifying theory that provides some of the underlying type system mechanics that make them both safe and work well together.--><p>这个提案里描述了一种方式，去解决这个领域面临的其中一个问题 – 如何对结构化并发和 Actor 消息传递的值进行类型检查。因此，这是一个统一的理论，它提供了一些基本的类型系统机制，使它们既保障安全又能很好地协同工作。</p><!--This implementation approach involves a marker protocol named `Sendable`, as well as a `@Sendable` attribute that may be applied to functions.--><p>这种实现方式会提供一个名为 <code>Sendable</code> 的 marker 协议，以及一个可应用于函数的 <code>@Sendable</code> 注解。</p><span id="more"></span><h2 id="背景故事"><a href="#背景故事" class="headerlink" title="背景故事"></a>背景故事</h2><!--Each actor instance and structured concurrency task in a program represents an “island of single threaded-ness”, which makes them a natural synchronization point that holds a bag of mutable state. These perform computation in parallel with other tasks, but we want the vast majority of code in such a system to be synchronization free — building on the logical independence of the actor, and using its mailbox as a synchronization point for its data.--><p>程序中的每个 actor 实例和结构化并发任务都代表着一个”单线程性的孤岛”（island of single threaded-ness），这使得它们成为一个自然的同步点，持有一系列可变的状态。这些任务与其他任务并行进行计算，但我们希望这样一个系统中的绝大多数代码都是非同步的 – 建立在 actor 的逻辑独立性之上，将其邮箱作为数据的同步点。</p><!--As such, a key question is: “when and how do we allow data to be transferred between concurrency domains?” Such transfers occur in arguments and results of actor method calls and tasks created by structured concurrency, for example.--><p>因此，一个关键问题是：”我们何时以及如何允许数据在并发域之间传输？” 例如，这种转移发生在 actor 方法调用的参数和返回值中，以及由结构化并发创建的任务中。</p><!--The Swift Concurrency features aspire to build a safe and powerful programming model. We want to achieve three things:--><p>Swift Concurrency 的功能渴望建立一个安全而强大的编程模型。我们希望实现这三件事：</p><!--1. We want Swift programmers to get a static compiler error when they try to pass across concurrency domains that could introduce unprotected shared mutable state.2. We want advanced programmers to be able to implement libraries with sophisticated techniques (e.g. a concurrent hash table) that can be used in a safe way by others.3. We need to embrace the existing world, which contains a lot of code that wasn’t designed with the Swift Concurrency model in mind. We need a smooth and incremental migration story.--><ol><li>我们希望 Swift 程序员在试图 跨并发域传递 可能引入不受保护的共享可变状态 时得到一个静态的编译器错误。</li><li>我们希望高阶程序员能够实现包含复杂技术的库（例如 ConcurrentHashTable），并且能够让其他人以一种安全的方式去使用。</li><li>我们需要拥抱现有的世界，其中包含了很多在设计时没有考虑到 Swift 并发模型的代码。我们需要一个平滑和渐进的迁移策略。</li></ol><!--Before we jump into the proposed solution, let’s take a look at some common cases that we would like to be able to model along with the opportunities and challenges of each. This will help us reason about the design space we need to cover.--><p>在我们进入提议的解决方案之前，先看一下我们希望能够建模的一些常见案例，以及每个案例中包含的改进机会和挑战。这将有助于推理出我们需要覆盖的设计空间。</p><h3 id="💖-Swift-值语义"><a href="#💖-Swift-值语义" class="headerlink" title="💖 Swift + 值语义"></a>💖 Swift + 值语义</h3><!--The first kind of type we need to support are simple values like integers. These can be trivially passed across concurrency domains because they do not contain pointers.--><p>我们需要支持的第一种类型是像 Integer 这样的简单值。这些类型可以简单地跨并发域传递，因为它们不包含指针。</p><!--Going beyond this, Swift has a strong emphasis on types with [value semantics](https://en.wikipedia.org/wiki/Value_semantics), which are safe to transfer across concurrent boundaries. Except for classes, Swift’s mechanisms for type composition provide value semantics when their elements do. This includes generic structs, as well as its core collections: for example, `Dictionary<Int, String>` can be directly shared across concurrency domains. Swift’s Copy on Write approach means that collections can be transferred without proactive data copying of their representations — an extremely powerful fact that I believe will make the Swift concurrency model more efficient than other systems in practice.--><p>除此之外，Swift 还非常强调具有<a href="https://en.wikipedia.org/wiki/Value_semantics">值语义</a>的类型，这些类型可以安全地跨并发边界传输。除了 class 之外，Swift 的类型组合机制在其元素提供值语义时也提供值语义。这包含了通用的结构，以及其核心集合：例如 <code>Dictionary&lt;Int, String&gt;</code> 就可以直接跨并发域共享。Swift 的 Copy on Write 机制意味着可以在不主动复制数据的情况下传输集合，这是一个非常强大的事实，我相信这将使 Swift 的并发模型在实践中比其他系统更加高效。</p><!--However, everything isn’t simple here: the core collections can **not** be safely transferred across concurrency domains when they contain general class references, closures that capture mutable state, and other non-value types. We need a way to differentiate between the cases that are safe to transfer and those that are not.--><p>然而，这里描述的一切并不简单：当核心集合包含了一般的 class 引用，捕获可变状态的闭包以及其他非值类型时，它们<strong>不能</strong>安全地跨并发域传输。我们需要一种方法来区分那些 可以安全转移的情况 和 不能安全转移的情况。</p><h3 id="值语义的组合"><a href="#值语义的组合" class="headerlink" title="值语义的组合"></a>值语义的组合</h3><!--Structs, enums and tuples are the primary mode for composition of values in Swift. These are all safe to transfer across concurrency domains — so long as the data they contain is itself safe to transfer.--><p>struct、enum 和 tuple 是 Swift 数值组合的主要模式。这些都可以安全地跨并发域传输 – 只要它们包含的数据本身可以安全地传输。</p><h3 id="高阶函数编程"><a href="#高阶函数编程" class="headerlink" title="高阶函数编程"></a>高阶函数编程</h3><!--It is common in Swift and other languages with functional programming roots to use [higher-order programming](https://en.wikipedia.org/wiki/Higher-order_function), where you pass functions to other functions.  Functions in Swift are reference types, but many functions are perfectly safe to pass across concurrency domains — for example, those with an empty capture list.--><p>在 Swift 和其他具有函数式编程基础的语言中，使用<a href="https://en.wikipedia.org/wiki/Higher-order_function">高阶编程</a>是很常见的，即把函数传递给其他函数。Swift 中的函数是引用类型，但许多函数是完全可以跨并发域传递的 – 例如，那些没有捕获变量的函数。</p><!--There are many useful reasons why you’d want to send bits of computation between concurrency domains in the form of a function — even trivial algorithms like `parallelMap` need this.  This occurs at larger scale as well — for example, consider an actor example like this:--><p>你会有很多很多合理的理由，想要在并发域之间以函数的形式发送计算过程 – 即使是像 <code>parallelMap</code> 这样的微不足道的算法也需要。这在更大规模的计算里也会发生 – 例如，考虑这样一个 actor 的例子：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">MyContactList</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">filteredElements</span>(<span class="keyword">_</span> <span class="params">fn</span>: (<span class="type">ContactElement</span>) -&gt; <span class="type">Bool</span>) <span class="keyword">async</span> -&gt; [<span class="type">ContactElement</span>] &#123; <span class="operator">…</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Which could then be used like so:--><p>能够像这样使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 没有捕获变量的闭包是没问题的！</span></span><br><span class="line">list <span class="operator">=</span> <span class="keyword">await</span> contactList.filteredElements &#123; <span class="variable">$0</span>.firstName <span class="operator">!=</span> <span class="string">&quot;Max&quot;</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 捕获了一个 `searchName` 字符串变量的闭包也是可以的</span></span><br><span class="line"><span class="comment">// 因为 String 是可以跨并发域传递的</span></span><br><span class="line">list <span class="operator">=</span> <span class="keyword">await</span> contactList.filteredElements &#123;</span><br><span class="line">  [searchName] <span class="keyword">in</span> <span class="variable">$0</span>.firstName <span class="operator">==</span> searchName</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--We feel that it is important to enable functions to be passed across concurrency domains, but we are also concerned that we should not allow capturing local state _by reference_ in these functions, and we should not allow capturing unsafe things by value.  Both would introduce memory safety problems.--><p>我们觉得让函数跨并发域传递是很重要的，但是同时我们也有顾虑，我们不应该允许在这些函数中<strong>通过引用</strong>来捕获本地状态，也不应该允许通过值来捕获不安全的东西。这两者都会带来内存安全问题。</p><h3 id="不可变-class"><a href="#不可变-class" class="headerlink" title="不可变 class"></a>不可变 class</h3><!--One common and efficient design pattern in concurrent programming is to build immutable data structures — it is perfectly safe to transfer a reference to a class across concurrency domains if the state within it never mutates. This design pattern is extremely efficient (no synchronization beyond ARC is required), can be used to build [advanced data structures](https://en.wikipedia.org/wiki/Persistent_data_structure), and is widely explored by the pure-functional language community.--><p>并发编程中一个常见的高效设计模式是建立不可变的数据结构 – 如果一个类中的状态永远不会发生变化，那么在不同的并发域中传输对该类的引用是完全安全的。这种设计模式非常高效（不需要 ARC 以外的同步），可以用来构建<a href="https://en.wikipedia.org/wiki/Persistent_data_structure">高级数据结构</a>，这种模式被纯函数式语言社区广泛采用。</p><h3 id="内部同步的引用类型"><a href="#内部同步的引用类型" class="headerlink" title="内部同步的引用类型"></a>内部同步的引用类型</h3><!--A common design pattern in concurrent systems is for a class to provide a “thread-safe” API: they protect their state with explicit synchronization (mutexes, atomics, etc). Because the public API to the class is safe to use from multiple concurrency domains, the reference to the class can be directly transferred safely.--><p>并发系统中另一种常见的设计模式就是让一个类提供一个“线程安全”的 API：它们用显式同步（mutexes、atomics 等）来保护它们的状态。因为该类的公开 API 可以从多个并发域中安全使用，所以对该类的引用可以安全地直接转移。</p><!--References to actor instances themselves are an example of this: they are safe to pass between concurrency domains by passing a pointer, since the mutable state within an actor is implicitly protected by the actor mailbox.--><p>对 actor 实例本身的引用就是一个例子：通过传递指针，它们可以在并发域之间安全传递，因为 actor 内部的可变状态是由 actor 邮箱隐式保护的。</p><h3 id="在并发域之间”传递”对象"><a href="#在并发域之间”传递”对象" class="headerlink" title="在并发域之间”传递”对象"></a>在并发域之间”传递”对象</h3><!--A fairly common pattern in concurrent systems is for one concurrency domain to build up a data structure containing unsynchronized mutable state, then “hand it off” to a different concurrency domain to use by transferring the raw pointer. This is correct without synchronization if (and only if) the sender stops using the data that it built up — the result is that only the sender or receiver dynamically accesses the mutable state at a time.--><p>并发系统中的一种相当常见的模式是，一个并发域建立了一个包含非同步的 可变状态的 数据结构，然后通过传输原始指针将其”移交”给另一个并发域使用。如果（也只有在）发送方停止使用已移交的数据时，这在没有同步的情况下也是正确的 – 结果是每次只有发送方或接收方动态地访问可变状态。</p><!--There are both safe and unsafe ways to achieve this, e.g. see the discussion about “exotic” type systems in the [Alternatives Considered](#alternatives-considered) section at the end.--><p>既有安全也有不安全的方式来实现这一点，例如，请看最后的其它备选方案部分中关于”奇异的类型系统功能“的讨论。</p><h3 id="深拷贝的-Class"><a href="#深拷贝的-Class" class="headerlink" title="深拷贝的 Class"></a>深拷贝的 Class</h3><!--One safe way to transfer reference types is to make a deep copy of the data structures, ensuring that the source and destination concurrency domains each have their own copy of mutable state. This can be expensive for large structures, but is/was commonly used in some Objective-C frameworks.  General consensus is that this should be _explicit_, not something implicit in the definition of a type.--><p>转移引用类型的一种安全方式是对数据结构进行深度复制，确保源并发域和目标并发域都有自己的可变状态副本。这对大型结构来说可能很昂贵，但在一些 Objective-C 框架中是或曾经是常用的。一般的共识是，这应该是显式的，而不是隐含在类型定义中的东西。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><!--This is just a sampling of patterns, but as we can see, there are a wide range of different concurrent design patterns in widespread use. The design center of Swift around value types and encouraging use of structs is a very powerful and useful starting point, but we need to be able to reason about the complex cases as well — both for communities that want to be able express high performance APIs for a given domain but also because we need to work with legacy code that won’t get rewritten overnight.--><p>这只是一些模式的范例，但我们可以看到，有很多不同的并发设计模式在广泛使用。Swift 的设计中心围绕着值类型和鼓励使用 struct 形成了一个非常强大和有利的起点，但是我们也需要能够推导出复杂的情况 – 这既是为了那些希望能够为特定领域编写高性能 API 的社区，也是因为我们需要处理那些 不可能在一夜之间被重写的遗留代码。</p><!--As such, it is important to consider approaches that allow library authors to express the intent of their types, it is important for app programmers to be able to work with uncooperative libraries retroactively, and it is also important that we provide safety as well as unsafe escape hatches so we can all just “get stuff done” in the face of an imperfect world that is in a process of transition.--><p>因此，允许库作者表达其类型意图的方法是很重要的，应用开发者能够与现有不兼容的库一起相处也是很重要的，而且我们不仅需要提供安全的方式，也需要不安全的方式，这个世界正处于过渡阶段，我们需要在这个不完美的世界里”把任务完成”。</p><!--Finally, our goal is for Swift (in general and in this specific case) to be a highly principled system that is sound and easy to use. In 20 years, many new libraries will be built for Swift and its ultimate concurrency model. These libraries will be built around value semantic types, but should also allow expert programmers to deploy state of the art techniques like lock-free algorithms, use immutable types, or whatever other design pattern makes sense for their domain. We want users of these APIs to not have to care how they are implemented internally.--><p>最后，我们的目标是让 Swift（在一般情况下和在这个特定情况下）成为一个高度统一的系统，健全且易于使用。未来 20 年，许多新的库将会基于 Swift 及其最终的并发模型建立。这些库将围绕值语义类型建立，但也应该允许高级程序员部署最先进的技术，如无锁算法，使用不可变类型，或任何其它对他们的领域有意义的设计模式。我们希望这些 API 的用户不必关心它们在内部的实现方式。</p><h2 id="解决方案-具体设计"><a href="#解决方案-具体设计" class="headerlink" title="解决方案 + 具体设计"></a>解决方案 + 具体设计</h2><!--The high level design of this proposal revolves around a `Sendable` marker protocol, adoption of `Sendable` by standard library types, and a new `@Sendable` attribute for functions.--><p>这个提案的上层设计围绕着一个名为 <code>Sendable</code> 的 marker 协议，标准库类型将会全面采用 <code>Sendable</code> 协议，以及一个新的 <code>@Sendable</code> 函数注解。</p><!--Beyond the basic proposal, in the future it could make sense to add a set of adapter types to handle legacy compatibility cases, and first class support for Objective-C frameworks.  These are described in the following section.--><p>除了基本的提案，在未来，有可能增加一组 Adapter 类型来处理遗留代码的兼容，以及对 Objective-C 框架的一级支持。这些将在下一节中讨论。</p><h3 id="Marker-协议类型"><a href="#Marker-协议类型" class="headerlink" title="Marker 协议类型"></a>Marker 协议类型</h3><!--This proposal introduces the concept of a “marker” protocol, which indicates that the protocol has some semantic property but is entirely a compile-time notion that does not have any impact at runtime.  Marker protocols have the following restrictions:--><p>本提案将引入 <code>marker</code> 协议的概念，这表明该协议具有某种语义属性，但完全是一个编译时的概念，在运行时没有任何影响。marker 协议有以下限制：</p><!--*   They cannot have requirements of any kind.*   They cannot inherit from non-marker protocols.*   A marker protocol cannot be named as the type in an `is` or `as?` check (e.g., `x as? Sendable` is an error).*   A marker protocol cannot be used in a generic constraint for a conditional protocol conformance to a non-marker protocol.--><ul><li>它们不能有任何形式的实现要求。</li><li>它们不能继承非 marker 协议。</li><li>marker 协议不能作为类型使用 <code>is</code> 或 <code>as?</code> 进行检查（例如，<code>x as? Sendable</code> 是一个错误）。</li><li>marker 协议不能用于非 marker 协议的 conditional conformance 约束中。</li></ul><!--We think this is a generally useful feature, but believe it should be a compiler-internal feature at this point.  As such, we explain it and use this concept with the “`@_marker`” attribute syntax below.--><p>我们认为这是一个通用的特性，但认为在这一点上它应该是一个编译器内部的特性。因此，我们在下面对它进行了解释，并通过 <code>@_marker</code> 注解语法来表达这个概念。</p><h3 id="Sendable-协议"><a href="#Sendable-协议" class="headerlink" title="Sendable 协议"></a><code>Sendable</code> 协议</h3><!--The core of this proposal is a marker protocol defined in the Swift standard library , which has special conformance checking rules:--><p>这个提案的核心是 Swift 标准库中定义的一个 marker 协议，它具有特殊的 conformance 检查规则：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@_marker</span></span><br><span class="line"><span class="keyword">protocol</span> <span class="title class_">Sendable</span> &#123;&#125;</span><br></pre></td></tr></table></figure><!--It is a good idea for types to conform to the `Sendable` protocol when they are designed so all of their public API is safe to use across concurrency domains.  This is true for example, when there are no public mutators, if public mutators are implemented with COW, or if they are implemented with internal locking or some other mechanism.  Types may of course have internal implementation details based on local mutation if they have locking or COW as part of their public API.--><p>当一个类型，其所有的 public API 都被设计成可以安全地跨并发域使用时，让它遵循 <code>Sendable</code> 协议就是个正确的选择。例如，当没有 public mutator 时，或者 public mutator 是用 COW 实现的，亦或者它们是用内部锁或其他机制实现的。类型当然也可以有本地 mutation 的内部实现细节，只要将 lock 或 COW 作为其公共 API 的一部分。</p><!--The compiler rejects any attempts to pass data across concurrency domains, e.g. rejecting cases where the argument or result of an actor message send or structured concurrency call does not conform to the `Sendable` protocol:--><p>编译器拒绝任何跨并发域传递数据的尝试，例如，actor 消息发送，结构化并发调用的参数或返回值不符合 <code>Sendable</code> 协议，这些情况都会遭到拒绝：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">SomeActor</span> &#123;</span><br><span class="line">  <span class="comment">// async functions are usable *within* the actor, so this</span></span><br><span class="line">  <span class="comment">// is ok to declare.</span></span><br><span class="line">  <span class="comment">// async 函数会在 actor 内部调用的，所以这是可以声明的</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">doThing</span>(<span class="params">string</span>: <span class="type">NSMutableString</span>) <span class="keyword">async</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ... but they cannot be called by other code not protected</span></span><br><span class="line"><span class="comment">// by the actor&#x27;s mailbox:</span></span><br><span class="line"><span class="comment">// ... 但如果它们从不被 actor 邮箱保护的地方被其它代码调用：</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">f</span>(<span class="params">a</span>: <span class="type">SomeActor</span>, <span class="params">myString</span>: <span class="type">NSMutableString</span>) <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="comment">// error: &#x27;NSMutableString&#x27; may not be passed across actors;</span></span><br><span class="line">  <span class="comment">//        it does not conform to &#x27;Sendable&#x27;</span></span><br><span class="line">  <span class="keyword">await</span> a.doThing(string: myString)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The `Sendable` protocol models types that are allowed to be safely passed across concurrency domains by copying the value.  This includes value-semantic types, references to immutable reference types, internally synchronized reference types, `@Sendable` closures, and potentially other future type system extensions for unique ownership etc.--><p><code>Sendable</code> 协议用于建模那些 允许通过复制值在并发域中安全传递的类型。这包括值语义类型、对不可变引用类型的引用、内部同步的引用类型、<code>@Sendable</code> 闭包，以及未来可能的其他类型系统扩展的唯一所有权等。</p><!--Note that incorrect conformance to this protocol can introduce bugs in your program (just as an incorrect implementation of `Hashable` can break invariants), which is why the compiler checks conformance (see below).--><p>请注意，对该协议不正确的 conformance 会在你的程序中引入错误（就像对 <code>Hashable</code> 的不正确实现会破坏不变性一样），这就是为什么编译器会检查 conformance（见下文）。</p><h4 id="元组的-Sendable-conformance"><a href="#元组的-Sendable-conformance" class="headerlink" title="元组的 Sendable conformance"></a>元组的 <code>Sendable</code> conformance</h4><!--Swift has [hard coded conformances for tuples](https://github.com/apple/swift-evolution/blob/main/proposals/0283-tuples-are-equatable-comparable-hashable.md) to specific protocols, and this should be extended to `Sendable`, when the tuples elements all conform to `Sendable`.--><p>Swift 的元组有特定协议的<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0283-tuples-are-equatable-comparable-hashable.md">硬编码 conformance</a>，<code>Sendable</code> 也应该加入到这个规则里，当元组的元素都符合 <code>Sendable</code> 时，元祖也遵循 <code>Sendable</code>。 </p><h4 id="元类型的-Sendable-conformance"><a href="#元类型的-Sendable-conformance" class="headerlink" title="元类型的 Sendable conformance"></a>元类型的 <code>Sendable</code> conformance</h4><!--Metatypes (such as` Int.Type`, the type produced by the expression `Int.self`) always conform to `Sendable`, because they are immutable.--><p>元类型（如 <code>Int.Type</code>，由表达式 <code>Int.self</code> 产生的类型）总是遵循 <code>Sendable</code>，因为它们是不可改变的。</p><h4 id="struct-和-enum-的-Sendable-conformance-检测"><a href="#struct-和-enum-的-Sendable-conformance-检测" class="headerlink" title="struct 和 enum 的 Sendable conformance 检测"></a>struct 和 enum 的 <code>Sendable</code> conformance 检测</h4><!--`Sendable` types are extremely common in Swift and aggregates of them are also safe to transfer across concurrency domains.  As such, the Swift compiler allows direct conformance to `Sendable` for structs and classes that are compositions of other `Sendable` types:--><p><code>Sendable</code> 类型在 Swift 中极为常见，它们的聚合体也可以安全地跨并发域传输。因此，Swift 编译器允许作为其他 <code>Sendable</code> 类型组合出来的 struct 和 class 直接遵循 <code>Sendable</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">MyPerson</span> : <span class="title class_ inherited__">Sendable</span> &#123; <span class="keyword">var</span> name: <span class="type">String</span>, age: <span class="type">Int</span> &#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyNSPerson</span> &#123; <span class="keyword">var</span> name: <span class="type">NSMutableString</span>, age: <span class="type">Int</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">SomeActor</span> &#123;</span><br><span class="line">  <span class="comment">// struct 和元组可以发送和接收</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">doThing</span>(<span class="params">x</span>: <span class="type">MyPerson</span>, <span class="params">y</span>: (<span class="type">Int</span>, <span class="type">Float</span>)) <span class="keyword">async</span> &#123;<span class="operator">..</span>&#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 如果跨 actor 边界被调用就会出错：MyNSPerson 不遵循 Sendable！</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">doThing</span>(<span class="params">x</span>: <span class="type">MyNSPerson</span>) <span class="keyword">async</span> &#123;<span class="operator">..</span>&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--While this is convenient, we would like to slightly increase friction of protocol adoption for cases that require more thought.  As such, the compiler rejects conformance of structs and enums to the `Sendable` protocol when one of their members (or associated values) does not itself conform to `Sendable` (or is not known to conform to `Sendable` through a generic constraint):--><p>虽然这很方便，但我们希望，在需要更加深思熟虑的情况下，稍微增加遵循协议的阻力。因此，当 struct 和 enum 的某个成员（或相关值）本身不符合 <code>Sendable</code> 协议（或通过约束无法推导出是否符合 <code>Sendable</code> 协议）时，编译器会拒绝让其遵循 <code>Sendable</code> 协议。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// error: MyNSPerson cannot conform to Sendable due to NSMutableString member.</span></span><br><span class="line"><span class="comment">// note: add &#x27;@unchecked&#x27; if you know what you&#x27;re doing.</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyNSPerson</span> : <span class="title class_ inherited__">Sendable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> name: <span class="type">NSMutableString</span></span><br><span class="line">  <span class="keyword">var</span> age: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// error: MyPair cannot conform to Sendable due to &#x27;T&#x27; member which may not itself be a Sendable</span></span><br><span class="line"><span class="comment">// note: see below for use of conditional conformance to model this</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyPair</span>&lt;<span class="type">T</span>&gt; : <span class="type">Sendable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> a, b: <span class="type">T</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 conditional conformance 来建模泛型类型</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyCorrectPair</span>&lt;<span class="type">T</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">var</span> a, b: <span class="type">T</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">MyCorrectPair</span>: <span class="title class_ inherited__">Sendable</span> <span class="keyword">where</span> <span class="title class_ inherited__">T</span>: <span class="title class_ inherited__">Sendable</span> &#123; &#125;</span><br></pre></td></tr></table></figure><!--As mentioned in the compiler diagnostic, any type can override this checking behavior by annotating the conformance to `Sendable` with `@unchecked`. This indicates that the type can safely be passed across concurrency domains, but requires the author of the type to ensure that this is safe.--><p>正如在编译器诊断信息里提到的，任何类型都可以通过用 <code>@unchecked</code> 对 <code>Sendable</code> 的 conformance 进行注解，来跳过这种检查行为。这表明该类型可以安全地跨并发域传递，但需要该类型的作者来确保这是安全的。</p><!--A `struct` or `enum` can only be made to conform to `Sendable` within the same source file in which the type was defined. This ensures that the stored properties in a struct and associated values in an enum are visible so that their types can be checked for `Sendable` conformance. For example:--><p>一个 <code>struct</code> 或 <code>enum</code> 只能在定义该类型的同一个源文件中实现 <code>Sendable</code> 的 conformance。这确保了 struct 中的存储属性和 enum 中的关联值是可见的，这样就可以检查它们的类型是否符合 <code>Sendable</code>。比如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// MySneakyNSPerson.swift</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MySneakyNSPerson</span> &#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">var</span> name: <span class="type">NSMutableString</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">var</span> age: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在另一个源文件或者模块中...</span></span><br><span class="line"><span class="comment">// error: cannot declare conformance to Sendable outside of</span></span><br><span class="line"><span class="comment">// the source file defined MySneakyNSPerson</span></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">MySneakyNSPerson</span>: <span class="title class_ inherited__">Sendable</span> &#123; &#125;</span><br></pre></td></tr></table></figure><!--Without this restriction, another source file or module, which cannot see the private stored property name, would conclude that `MySneakyNSPerson` is properly a `Sendable`. One can declare conformance to `Sendable` as `@unchecked` to disable this check as well:--><p>如果没有这个限制，另一个源文件或模块，无法看到私有的存储属性，就会得出结论，<code>MySneakyNSPerson</code> 可以遵循 <code>Sendable</code>。我们也可以将与 <code>Sendable</code> 的遵循声明为 <code>@unchecked</code> 来禁用这个检查：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 在另一个源文件或者模块中...</span></span><br><span class="line"><span class="comment">// okay: 在另一个源文件的 unchecked 遵循是被允许的</span></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">MySneakyNSPerson</span>: @unchecked <span class="title class_ inherited__">Sendable</span> &#123; &#125;</span><br></pre></td></tr></table></figure><h4 id="struct-enum-的-Sendable-隐式-conformance"><a href="#struct-enum-的-Sendable-隐式-conformance" class="headerlink" title="struct&#x2F;enum 的 Sendable 隐式 conformance"></a>struct&#x2F;enum 的 <code>Sendable</code> 隐式 conformance</h4><!--Many structs and enums satisfy the requirements of `Sendable`, and having to explicitly write out "`: Sendable`" for every such type can feel like boilerplate. --><p>许多 struct 和 enum 都满足 <code>Sendable</code> 的实现要求，如果需要为每个类型都明确写出 “<code>: Sendable</code>“会让人觉得是太啰嗦。</p><!--For non-public structs and enums that are also not `@usableFromInline`, and for frozen public structs and enums, the `Sendable` conformance is implicitly provided when conformance checking (described in the previous section) succeeds:--><p>对于不属于 <code>@usableFromInline</code> 的非 public struct 和 enum，以及 frozen 的 public struct 和 enum，当 conformance 检查（在上一节中描述）成功时，将隐式得遵循 <code>Sendable</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">MyPerson2</span> &#123; <span class="comment">// 隐式遵循 Sendable</span></span><br><span class="line">  <span class="keyword">var</span> name: <span class="type">String</span>, age: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NotConcurrent</span> &#123; &#125; <span class="comment">// 不遵循 Sendable</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyPerson3</span> &#123; <span class="comment">// 不遵循 Sendable 因为 nc 不是 Sendable 的类型</span></span><br><span class="line">  <span class="keyword">var</span> nc: <span class="type">NotConcurrent</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Public non-frozen structs and enums do not get an implicit conformance, because doing so would present a problem for API resilience: the implicit conformance to `Sendable` would become part of the contract with clients of the API, even if it was not intended to be. Moreover, this contract could easily be broken by extending the struct or enum with storage that does not conform to `Sendable`. --><p>public 的非 fronzen struct 和 enum 不会得到隐式的遵循，因为这样做会给 API resilience 带来问题：对 <code>Sendable</code> 的隐式的遵循会成为与 API 客户端约定的一部分，即使它不是有意的。此外，这种约定可以很容易地被破坏，因为增加 struct 或 enum 的存储属性可能会破坏 <code>Sendable</code> 的遵循。</p><!--> **Rationale**: Existing precedent from `Hashable`, `Equatable`, and `Codable` is to require explicit conformance, even when the details are synthesized. We break from that precedent for `Sendable` because (1) `Sendable` is likely to be even more common, (2) there is no impact on code size (or the binary at all) for `Sendable`, unlike with the other protocols, and (3) `Sendable` does not introduce any additional API beyond allowing the use of the type across concurrency domains.--><blockquote><p><strong>理由</strong>：来自 <code>Hashable</code>、<code>Equatable</code> 和 <code>Codable</code> 的现有先例是要求有显式的 conformance ，即使实现细节会被合成。我们为 <code>Sendable</code> 打破了这个先例，因为(1) <code>Sendable</code> 可能会更加普遍，(2) <code>Sendable</code> 对代码大小（或二进制）没有影响，与其他协议不同，(3) <code>Sendable</code> 除了允许跨并发域使用该类型外，没有引入任何额外的 API。</p></blockquote><!--Note that implicit conformance to `Sendable` is only available for non-generic types and for generic types whose instance data is guaranteed to be of `Sendable` type. For example:--><p>请注意，对 <code>Sendable</code> 的隐式遵循只适用于非泛型类型和实例数据保证为 <code>Sendable</code> 的泛型类型。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">X</span>&lt;<span class="type">T</span>: <span class="type">Sendable</span>&gt; &#123;  <span class="comment">// 隐式遵循 Sendable</span></span><br><span class="line">  <span class="keyword">var</span> value: <span class="type">T</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Y</span>&lt;<span class="type">T</span>&gt; &#123;    <span class="comment">// 无法隐式遵循 Sendable 因为 T 不遵循 Sendable</span></span><br><span class="line">  <span class="keyword">var</span> value: <span class="type">T</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Swift will not implicitly introduce a conditional conformance. It is possible that this could be introduced in a future proposal.--><p>Swift 将不会隐式地引入 conditional conformance。这有可能在未来的提案中被引入。</p><h4 id="class-的-Sendable-conformance-检测"><a href="#class-的-Sendable-conformance-检测" class="headerlink" title="class 的 Sendable conformance 检测"></a>class 的 <code>Sendable</code> conformance 检测</h4><!--Any class may be declared to conform to `Sendable` with an `@unchecked` conformance, allowing them to be passed between actors without semantic checks.  This is appropriate for classes that use access control and internal synchronization to provide memory safety — these mechanisms cannot generally be checked by the compiler.--><p>任何 class 都可以被声明为符合 <code>Sendable</code> 的 <code>@unchecked</code> 遵循，允许它们在 actor 之间传递而不需要语义检查。这适用于使用访问权限和内部同步来提供内存安全的类 – 这些机制一般无法通过编译器检查。</p><!--In addition, a class may conform to `Sendable` and be checked for memory safety by the compiler in a specific limited case: when the class is a final class containing only immutable stored properties of types that conform to Sendable:--><p>此外，一个 class 也许可以遵循 <code>Sendable</code>，并在特定的有限情况下通过编译器检查内存安全：当该 class 是一个 final class，只包含遵循 Sendable 的不可变存储属性：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> : <span class="title class_ inherited__">Sendable</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> state: <span class="type">String</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Such classes may not inherit from classes other than NSObject (for Objective-C interoperability).  `Sendable` classes have the same restriction as structs and enums that requires the `Sendable` conformance to occur in the same source file.--><p>这样的 class 不能继承于除 NSObject 以外的类（为了 Objective-C 的互操作性）。<code>Sendable</code> class 与 struct 和 enum 有相同的限制，要求 <code>Sendable</code> 的 conformance 写在同一个源文件中。</p><!--This behavior makes it possible to safely create and pass around immutable bags of shared state between actors.  There are several ways to generalize this in the future, but there are non-obvious cases to nail down.  As such, this proposal intentionally keeps safety checking for classes limited to ensure we make progress on other aspects of the concurrency design.--><p>这种行为使得在 actor 之间安全地创建和传递不可变的共享状态成为可能。在未来，有几种方法可以将其泛化，但有一些不明显的情况需要确定下来。因此，本提案有意保持对 class 的安全检查，以确保我们可以在并发性设计的其他方面取得进展。</p><h4 id="Actor-类型"><a href="#Actor-类型" class="headerlink" title="Actor 类型"></a>Actor 类型</h4><!--Actor types provide their own internal synchronization, so they implicitly conform to `Sendable`. The [actors proposal](https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md) provides more detail.--><p>Actor 类型提供它们自己的内部同步，所以它们隐式得遵循 <code>Sendable</code>。<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md">actors 提案</a>提供了更多细节。</p><h4 id="Key-path-字面量"><a href="#Key-path-字面量" class="headerlink" title="Key path 字面量"></a>Key path 字面量</h4><!--Key paths themselves conform to the `Sendable` protocol. However, to ensure that it is safe to share key paths, key path literals can only capture values of types that conform to the `Sendable` protocol. This affects uses of subscripts in key paths:--><p>Key path 本身符合 <code>Sendable</code> 协议。然而，为了确保共享 key path 的安全性，key path 字面量只能捕获符合 <code>Sendable</code> 协议的类型的值。这会影响 key path 下标的使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">SomeClass</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> value: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SomeContainer</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> dict: [<span class="type">SomeClass</span> : <span class="type">String</span>]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> sc <span class="operator">=</span> <span class="type">SomeClass</span>(<span class="operator">...</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// error: capture of &#x27;sc&#x27; in key path requires &#x27;SomeClass&#x27; to conform</span></span><br><span class="line"><span class="comment">// to &#x27;Sendable&#x27;</span></span><br><span class="line"><span class="keyword">let</span> keyPath <span class="operator">=</span> \<span class="type">SomeContainer</span>.dict[sc]</span><br></pre></td></tr></table></figure><h3 id="新的-Sendable-函数注解"><a href="#新的-Sendable-函数注解" class="headerlink" title="新的 @Sendable 函数注解"></a>新的 <code>@Sendable</code> 函数注解</h3><!--While the `Sendable` protocol directly addresses value types and allows classes to opt-in to participation with the concurrency system, function types are also important reference types that cannot currently conform to protocols. Functions in Swift occur in several forms, including global func declarations, nested functions, accessors (getters, setters, subscripts, etc), and closures.  It is useful and important to allow functions to be passed across concurrency domains where possible to allow higher order functional programming techniques in the Swift Concurrency model, for example to allow definition of `parallelMap` and other obvious concurrency constructs.--><p>虽然 <code>Sendable</code> 协议直接针对值类型，并允许 class 选择性地参与并发系统，但函数类型也是重要的引用类型，目前无法遵循协议。Swift 中的函数有几种形式，包括全局 func 声明、嵌套函数、访问器（getters、setters、subscripts 等）和闭包。在可能的情况下，允许函数跨并发域传递，以允许 Swift 并发模型中的高阶函数式编程技术，例如允许定义 <code>parallelMap</code> 和其他明显的并发结构，这是非常实用和重要的。</p><!--We propose defining a new attribute on function types named `@Sendable`.   A `@Sendable` function type is safe to transfer across concurrency domains (and thus, it implicitly conforms to the `Sendable` protocol).  To ensure memory safety, the compiler checks several things about values (e.g. closures and functions) that have `@Sendable` function type:--><p>我们建议在函数类型上定义一个名为 <code>@Sendable</code> 的新注解。一个 <code>@Sendable</code> 函数类型可以安全地跨并发域传输（因此，它隐式得遵循 <code>Sendable</code> 协议）。为了确保内存安全，编译器会对 <code>@Sendable</code> 函数类型的值（例如闭包和函数）进行若干检查：</p><!--1.  A function can be marked `@Sendable`. Any captures must also conform to `Sendable`.2.  Closures that have `@Sendable` function type can only use by-value captures. Captures of immutable values introduced by `let` are implicitly by-value; any other capture must be specified via a capture list:    ```swift    let prefix: String = ...    var suffix: String = ...    strings.parallelMap &#123; [suffix] in prefix + $0 + suffix &#125;    ```    The types of all captured values must conform to `Sendable`.3.  Accessors are not currently allowed to participate with the `@Sendable` system as of this proposal.  It would be straight-forward to allow getters to do so in a future proposal if there was demand for this.--><ol><li><p>一个函数可以被标记为 <code>@Sendable</code>。所有捕获的值都必须遵循 <code>Sendable</code>。</p></li><li><p><code>@Sendable</code> 函数类型的闭包只能使用 by-value 捕获。由 <code>let</code> 引入的不可改变的值的捕获是隐含的 by-value；任何其他捕获的变量必须通过捕获列表指定：</p> <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">prefix</span>: <span class="type">String</span> <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">var</span> suffix: <span class="type">String</span> <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line">strings.parallelMap &#123; [suffix] <span class="keyword">in</span> <span class="keyword">prefix</span> <span class="operator">+</span> <span class="variable">$0</span> <span class="operator">+</span> suffix &#125;</span><br></pre></td></tr></table></figure><p> 所有捕获的值的类型必须遵循 <code>Sendable</code>。</p></li><li><p>目前在本提案中，getter&#x2F;setter 不允许参与 <code>@Sendable</code> 系统。如果有这方面的需求，可以在未来的提案里直接允许 getter 声明为 Sendable。</p></li></ol><!--The `@Sendable` attribute to function types is orthogonal to the existing `@escaping` attribute, but it works the same way.  `@Sendable` functions are always subtypes of non-`@Sendable` functions, and implicitly convert when needed.  Similarly, closure expressions infer the `@Sendable` bit from context just like `@escaping` closures do.--><p>函数类型的 <code>@Sendable</code> 注解与现有的 <code>@escaping</code> 注解是正交的，但其工作方式是一样的。<code>@Sendable</code> 函数总是非 <code>@Sendable</code> 函数的子类型，并在需要时隐式地进行转换。同样地，闭包表达式从上下文中推断出 <code>@Sendable</code> 位，就像 <code>@escaping</code> 闭包所做的那样。</p><!--We can revisit the example from the motivation section — it may be declared like this:--><p>我们可以重温一下前文提到的例子 – 它可以这样声明：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">MyContactList</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">filteredElements</span>(<span class="keyword">_</span> <span class="params">fn</span>: <span class="keyword">@Sendable</span> (<span class="type">ContactElement</span>) -&gt; <span class="type">Bool</span>) <span class="keyword">async</span> -&gt; [<span class="type">ContactElement</span>] &#123; <span class="operator">…</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Which could then be used like so:--><p>然后可以像这样使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 没有捕获变量的闭包是没问题的！</span></span><br><span class="line">list <span class="operator">=</span> <span class="keyword">await</span> contactList.filteredElements &#123; <span class="variable">$0</span>.firstName <span class="operator">!=</span> <span class="string">&quot;Max&quot;</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 捕获了一个 `searchName` 字符串也是没问题的，因为字符串遵循 Sendable</span></span><br><span class="line"><span class="comment">// searchName 是隐式的值捕获</span></span><br><span class="line">list <span class="operator">=</span> <span class="keyword">await</span> contactList.filteredElements &#123; <span class="variable">$0</span>.firstName<span class="operator">==</span>searchName &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// @Sendable 是类型的一部分，所以传递一个兼容的函数声明也没问题！</span></span><br><span class="line">list <span class="operator">=</span> <span class="keyword">await</span> contactList.filteredElements(dynamicPredicate)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Error: cannot capture NSMutableString in a @Sendable closure!</span></span><br><span class="line">list <span class="operator">=</span> <span class="keyword">await</span> contactList.filteredElements &#123;</span><br><span class="line">  <span class="variable">$0</span>.firstName <span class="operator">==</span> nsMutableName</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Error: someLocalInt cannot be captured by reference in a</span></span><br><span class="line"><span class="comment">// @Sendable closure!</span></span><br><span class="line"><span class="keyword">var</span> someLocalInt <span class="operator">=</span> <span class="number">1</span></span><br><span class="line">list <span class="operator">=</span> <span class="keyword">await</span> contactList.filteredElements &#123;</span><br><span class="line">  someLocalInt <span class="operator">+=</span> <span class="number">1</span></span><br><span class="line">  <span class="keyword">return</span> <span class="variable">$0</span>.firstName <span class="operator">==</span> searchName</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The combination of `@Sendable` closures and `Sendable` types allows type safe concurrency that is library extensible, while still being easy to use and understand.  Both of these concepts are key foundations that actors and structured concurrency builds on top of.--><p><code>@Sendable</code> 闭包和 <code>Sendable</code> 类型的组合允许类型安全的并发，它是可扩展的库，同时仍然易于使用和理解。这两个概念都是关键的基础，actor 和结构化并发都建立在其之上。</p><h4 id="闭包表达式的-Sendable-推导"><a href="#闭包表达式的-Sendable-推导" class="headerlink" title="闭包表达式的 @Sendable 推导"></a>闭包表达式的 <code>@Sendable</code> 推导</h4><!--The inference rule for `@Sendable` attribute for closure expressions is similar to closure `@escaping` inference.  A closure expression is inferred to be `@Sendable` if:--><p>闭包表达式的 <code>@Sendable</code> 注解推导规则与闭包 <code>@escaping</code> 相似。在以下几种情况中，一个闭包表达式将被推导为 <code>@Sendable</code>：</p><!--*   It is used in a context that expects a `@Sendable` function type (e.g. `parallelMap` or `Task.runDetached`).*   When `@Sendable` is in the closure “in” specification.--><ul><li>它被用于期望有 <code>@Sendable</code> 函数类型的上下文中（例如 <code>parallelMap</code> 或 <code>Task.runDetached</code>）。</li><li>当 <code>@Sendable</code> 在闭包的 <code>in</code> 前进行指定。</li></ul><!--The difference from `@escaping` is that a context-less closure defaults to be non-`@Sendable`, but defaults to being `@escaping`:--><p>与 <code>@escaping</code> 的区别在于，无上下文的闭包默认为非 <code>@Sendable</code> 的，但默认为 <code>@escaping</code>。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// defaults to @escaping but not @Sendable</span></span><br><span class="line"><span class="keyword">let</span> fn <span class="operator">=</span> &#123; (x: <span class="type">Int</span>, y: <span class="type">Int</span>) -&gt; <span class="type">Int</span> <span class="keyword">in</span> x<span class="operator">+</span>y &#125;</span><br></pre></td></tr></table></figure><!--Nested functions are also an important consideration, because they can also capture values just like a closure expression.  The `@Sendable` attribute is used on nested function declarations to opt-into concurrency checking:--><p>嵌套函数也是一个重要的考虑因素，因为它们也可以像闭包表达式一样捕获值。嵌套函数声明中使用了 <code>@Sendable</code> 注解来选择加入并发检查：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">globalFunction</span>(<span class="params">arr</span>: [<span class="type">Int</span>]) &#123;</span><br><span class="line">  <span class="keyword">var</span> state <span class="operator">=</span> <span class="number">42</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// Error, &#x27;state&#x27; is captured immutably because closure is @Sendable.</span></span><br><span class="line">  arr.parallelForEach &#123; state <span class="operator">+=</span> <span class="variable">$0</span> &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Ok, function captures &#x27;state&#x27; by reference.</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">mutateLocalState1</span>(<span class="params">value</span>: <span class="type">Int</span>) &#123;</span><br><span class="line">    state <span class="operator">+=</span> value</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Error: non-@Sendable function isn&#x27;t convertible to @Sendable function type.</span></span><br><span class="line">  arr.parallelForEach(mutateLocalState1)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">@Sendable</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">mutateLocalState2</span>(<span class="params">value</span>: <span class="type">Int</span>) &#123;</span><br><span class="line">    <span class="comment">// Error: &#x27;state&#x27; is captured as a let because of @Sendable</span></span><br><span class="line">    state <span class="operator">+=</span> value</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Ok, mutateLocalState2 is @Sendable.</span></span><br><span class="line">  arr.parallelForEach(mutateLocalState2)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This composes cleanly for both structured concurrency and actors.--><p>这对结构化并发和 actor 来说都是干净的组合。</p><h3 id="Thrown-errors"><a href="#Thrown-errors" class="headerlink" title="Thrown errors"></a>Thrown errors</h3><!--A function or closure that `throws` can effectively return a value of any type that conforms to the `Error` protocol. If the function is called from a different concurrency domain, the thrown value can be passed across it.--><p>一个 <code>throws</code> 的函数或闭包可以有效地返回一个符合 <code>Error</code> 协议的任何类型的值。如果该函数从不同的并发域被调用，抛出的值可以被传递到另一个作用域：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MutableStorage</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> counter: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">ProblematicError</span>: <span class="title class_ inherited__">Error</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> storage: <span class="type">MutableStorage</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">MyActor</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> storage: <span class="type">MutableStorage</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">doSomethingRisky</span>() <span class="keyword">throws</span> -&gt; <span class="type">String</span> &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="type">ProblematicError</span>(storage: storage)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--A call to `myActor.doSomethingRisky()` from another concurrency domain would throw the problematic error, capturing part of the mutable state of `myActor`, then provide it to another concurrency domain, breaking actor isolation. Because there is no information in the signature of `doSomethingRisky()` about the types of errors thrown, and an error that propagates out from `doSomethingRisky()` could come from _any_ code that the function invokes, there is no place at which we could check that only `Sendable`-conforming errors are thrown.--><p>从另一个并发域调用 <code>myActor.doSomethingRisky()</code> 会抛出有问题的 error ，它捕获了 <code>myActor</code> 的部分可变状态，然后提供给另一个并发域，破坏了 actor 的隔离。因为 <code>doSomethingRisky()</code> 的签名中没有关于抛出的 Error 类型的信息，而且从 <code>doSomethingRisky()</code> 传播出来的 error 可能来自该函数调用的任何代码，所以我们没有地方可以检查被抛出的 error 是否遵循 <code>Sendable</code>。</p><!--To close this safety hole, we alter the definition of the `Error` protocol to require that _all_ error types conform to `Sendable`:--><p>为了修复这个安全漏洞，我们改变了 <code>Error</code> 协议的定义，要求所有的 error 类型都遵循 <code>Sendable</code>。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Error</span>: <span class="title class_ inherited__">Sendable</span> &#123; <span class="operator">…</span> &#125;</span><br></pre></td></tr></table></figure><!--Now, the `ProblematicError` type will be rejected with an error because it conforms to `Sendable` but contains a stored property of non-`Sendable` type `MutableStorage`.--><p>现在，<code>ProblematicError</code> 类型将被编译器拒绝并抛出错误，因为它尝试声明为 <code>Sendable</code>，但包含一个非 <code>Sendable</code> 类型 <code>MutableStorage</code> 的存储属性。</p><!--Generally speaking, one cannot add a new inherited protocol to an existing protocol without breaking both source and binary compatibility. However, marker protocols have no impact on the ABI and no requirements, so binary compatibility is maintained.--><p>一般来说，在不破坏代码和二进制兼容性的情况下，我们不能在现有的协议上添加新的协议继承。然而，marker 协议对 ABI 没有影响，也没有要求，所以二进制兼容性可以保持。</p><!--Source compatibility requires more care, however. `ProblematicError` is well-formed in today’s Swift, but will be rejected with the introduction of `Sendable`. To ease the transition, errors about types that get their `Sendable` conformances through `Error` will be downgraded to warnings in Swift &lt; 6.--><p>然而，代码兼容性还是需要更加留意。<code>ProblematicError</code> 在今天的 Swift 中是没有问题的代码，但随着 <code>Sendable</code> 的引入将被拒绝。为了便于过渡，在 Swift &lt; 6 中，通过 <code>Error</code> 获得 <code>Sendable</code> 符合性的类型的错误将被降级为警告。</p><h3 id="标准库类型全面添加-Sendable-的-conformance"><a href="#标准库类型全面添加-Sendable-的-conformance" class="headerlink" title="标准库类型全面添加 Sendable 的 conformance"></a>标准库类型全面添加 <code>Sendable</code> 的 conformance</h3><!--It is important for standard library types to be passed across concurrency domains. The vast majority of standard library types provide value semantics, and therefore should conform to `Sendable`, e.g.:--><p>对于标准库类型来说，跨并发域传递是很重要的。绝大多数标准库类型提供了值语义，因此也应该遵循 <code>Sendable</code>，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Int</span>: <span class="title class_ inherited__">Sendable</span> &#123;&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">String</span>: <span class="title class_ inherited__">Sendable</span> &#123;&#125;</span><br></pre></td></tr></table></figure><!--Generic value-semantic types are safe to be passed across concurrency domains so long as any element types are safe to be passed across concurrency domains. This dependency can be modeled by conditional conformances:--><p>只要所有泛型参数的类型可以安全地跨并发域传递，泛型的值语义类型就可以安全地跨并发域传递。这种先决条件可以通过 conditional conformance 来进行建模：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Optional</span>: <span class="title class_ inherited__">Sendable</span> <span class="keyword">where</span> <span class="title class_ inherited__">Wrapped</span>: <span class="title class_ inherited__">Sendable</span> &#123;&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Array</span>: <span class="title class_ inherited__">Sendable</span> <span class="keyword">where</span> <span class="title class_ inherited__">Element</span>: <span class="title class_ inherited__">Sendable</span> &#123;&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Dictionary</span>: <span class="title class_ inherited__">Sendable</span></span><br><span class="line">    <span class="keyword">where</span> <span class="title class_ inherited__">Key</span>: <span class="title class_ inherited__">Sendable</span>, <span class="title class_ inherited__">Value</span>: <span class="title class_ inherited__">Sendable</span> &#123;&#125;</span><br></pre></td></tr></table></figure><!--Except for the cases listed below, all struct, enum, and class types in the standard library conform to the `Sendable` protocol. Generic types conditionally conform to the `Sendable` protocol when all of their generic arguments conform to `Sendable`. The exceptions to these rules follow:--><p>除了下面列出的情况，标准库中的所有 struct、enum 和 class 的类型都将添加 <code>Sendable</code> 的 conformance。当泛型类型的所有泛型参数都遵循 <code>Sendable</code> 时，它们将有条件地遵循 <code>Sendable</code>。这些规则的例外情况如下：</p><!--*   `ManagedBuffer`: this class is meant to provide mutable reference semantics for a buffer. It must not conform to `Sendable` (even unsafely).*   `Unsafe(Mutable)(Buffer)Pointer`: these generic types _unconditionally_ conform to the `Sendable` protocol. This means that an unsafe pointer to a non-concurrent value can potentially be used to share such values between concurrency domains. Unsafe pointer types provide fundamentally unsafe access to memory, and the programmer must be trusted to use them correctly; enforcing a strict safety rule for one narrow dimension of their otherwise completely unsafe use seems inconsistent with that design.*   Lazy algorithm adapter types: the types returned by lazy algorithms (e.g., as the result of `array.lazy.map` { … }) never conform to `Sendable`. Many of these algorithms (like the lazy `map`) take non-`@Sendable` closure values, and therefore cannot safely conform to `Sendable`.--><ul><li><code>ManagedBuffer</code>: 这个类的目的是为一个缓冲区提供可变的引用语义。它不应该遵循 <code>Sendable</code>（甚至不应该跳过检查）。</li><li><code>unsafe(Mutable)(Buffer)Pointer</code>：这些泛型类型<strong>无条件地</strong>遵循 <code>Sendable</code> 协议。这意味着一个非并发值的 unsafe pointer 有可能被用来在并发域之间共享这些值。Unsafe 的 pointer 类型从根本上提供了对内存的不安全访问，必须相信程序员能够正确地使用它们；对它们的一个狭窄的维度强制执行严格的安全规则，否则完全不安全的使用似乎与该设计不一致。</li><li>Lazy 算法适配器类型：lazy 算法返回的类型（例如，作为 <code>array.lazy.map</code> { … } 的结果）从不符合 <code>Sendable</code>。许多这样的算法（如 lazy 的 <code>map</code>）采取非 <code>@Sendable</code> 的闭包，因此不能安全地符合<code>Sendable</code>。</li></ul><blockquote><p>译者注：虽然上面提到 unsafePointer 家族的类型都无条件遵循 <code>Sendable</code> 协议，后续的提案 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0331-remove-sendable-from-unsafepointer.md">SE-0331 Remove Sendable conformance from unsafe pointer types</a> 移除了这条规则，所以 unsafePointer 家族的类型现在并不遵循 <code>Sendable</code>。</p></blockquote><!--The standard library protocols `Error` and `CodingKey` inherit from the `Sendable` protocol:*   `Error` inherits from `Sendable` to ensure that thrown errors can safely be passed across concurrency domains, as discussed in the previous section.*   `CodingKey` inherits from `Sendable` so that types like `EncodingError` and `DecodingError`, which store `CodingKey` instances, can correctly conform to `Sendable`.--><p>标准库协议 <code>Error</code> 和 <code>CodingKey</code> 都添加 <code>Sendable</code> 协议的继承：</p><ul><li><code>Error</code> 继承 <code>Sendable</code>，以确保抛出的错误可以安全地跨并发域传递，如上一节所述。</li><li><code>CodingKey</code> 继承 <code>Sendable</code>，以便像 <code>EncodingError</code> 和 <code>DecodingError</code> 这样存储 <code>CodingKey</code> 实例的类型可以遵循 <code>Sendable</code>。</li></ul><h3 id="支持导入-C-Objective-C-的-API"><a href="#支持导入-C-Objective-C-的-API" class="headerlink" title="支持导入 C &#x2F; Objective-C 的 API"></a>支持导入 C &#x2F; Objective-C 的 API</h3><!--Interoperability with C and Objective-C is an important part of Swift. C code will always be implicitly unsafe for concurrency, because Swift cannot enforce correct behavior of C APIs. However, we still define some basic interactions with the concurrency model by providing implicit `Sendable` conformances for many C types:*   C enum types always conform to the `Sendable` protocol.*   C struct types conform to the `Sendable` protocol if all of their stored properties conform to `Sendable`.*   C function pointers conform to the `Sendable` protocol. This is safe because they cannot capture values.--><p>与 C 和 Objective-C 的互操作性是 Swift 的一个重要组成部分。由于 Swift 无法强制强制 C 语言 API 行为正确，因此 C 语言代码对于并发来说总是隐含着不安全的要素。然而，我们仍然通过为许多 C 类型提供隐式 <code>Sendable</code> 遵循来定义与并发模型的一些基本互动：</p><ul><li>C 的 enum 类型总是遵循 <code>Sendable</code> 协议。</li><li>C 的 struct 类型遵循 <code>Sendable</code> 协议，如果它们所有的存储属性都遵循 <code>Sendable</code>。</li><li>C 的函数指针遵循 <code>Sendable</code> 协议。这是很安全的，因为它们无法捕获值。</li></ul><h2 id="未来工作-后续项目"><a href="#未来工作-后续项目" class="headerlink" title="未来工作 &#x2F; 后续项目"></a>未来工作 &#x2F; 后续项目</h2><!--In addition to the base proposal, there are several follow-on things that could be explored as follow-on proposals.--><p>除了基本提案之外，还有几个后续的东西可以作为后续提案进行探讨。</p><h3 id="Adaptor-Types-for-Legacy-Codebases"><a href="#Adaptor-Types-for-Legacy-Codebases" class="headerlink" title="Adaptor Types for Legacy Codebases"></a>Adaptor Types for Legacy Codebases</h3><!--**NOTE**: This section is NOT considered part of the proposal — it is included just to illustrate aspects of the design.--><p><strong>注意</strong>。本节不属于提案的一部分 – 包含它只是为了说明设计的各个方面。</p><!--The proposal above provides good support for composition and Swift types that are updated to support concurrency.  Further, Swift’s support for retroactive conformance of protocols makes it possible for users to work with codebases that haven’t been updated yet.--><p>上面的提案为组合和 Swift 类型提供了良好的支持，这些类型将被更新以支持并发。此外，Swift 对跳过协议遵循的支持，让用户可以使用尚未更新的遗留代码库。</p><!--However, there is an additional important aspect of compatibility with existing frameworks that is important to confront: frameworks are sometimes designed around dense graphs of mutable objects with ad hoc structures.  While it would be nice to “rewrite the world” eventually, practical Swift programmers will need support to “get things done” in the meantime.  By analogy, when Swift first came out, most Objective-C frameworks were not audited for nullability.  We introduced “`ImplicitlyUnwrappedOptional`” to handle the transition period, which gracefully faded from use over the years.--><p>然而，在与现有框架的兼容性方面，还有一个重要的问题需要面对：框架有时是围绕着具有特殊结构的可变对象密集图设计的。虽然最终能”重写整个世界”是件好事，但实际工作中 Swift 程序员需要得到支持，以便在这期间”将工作完成”。</p><p>举个例子，当 Swift 刚出来的时候，大多数 Objective-C 框架都没有对 nullability 进行审核。我们引入了 “<code>ImplicitlyUnwrappedOptional</code>“ 来处理过渡期的问题，随着时间的推移，它优雅地淡出了使用范围。</p><!--To illustrate how we can do this with Swift concurrency, consider a pattern that is common in Objective-C frameworks: passing an object graph across threads by “transferring” the reference across threads — this is useful but not memory safe!  Programmers will want to be able to express these things as part of their actor APIs within their apps.--><p>为了说明我们如何在 Swift Concurrency 中做到这一点，请考虑 Objective-C 框架中常见的一种模式：通过跨线程”转移”引用来传递一个对象图 – 这很有用，但不符合内存安全！程序员会希望能够在他们的应用程序中把这些东西作为 actor API 的一部分来表达。</p><!--This can be achieved by the introduction of a generic helper struct:--><p>这可以通过引入一个通用的 helper struct 来实现：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@propertyWrapper</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">UnsafeTransfer</span>&lt;<span class="type">Wrapped</span>&gt; : <span class="keyword">@unchecked</span> <span class="type">Sendable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> wrappedValue: <span class="type">Wrapped</span></span><br><span class="line">  <span class="keyword">init</span>(<span class="params">wrappedValue</span>: <span class="type">Wrapped</span>) &#123;</span><br><span class="line">    <span class="keyword">self</span>.wrappedValue <span class="operator">=</span> wrappedValue</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--For example, `NSMutableDictionary` isn’t safe to pass across concurrency domains, so it isn’t safe to conform to `Sendable`.  The struct above allows you (as an app programmer) to write an actor API in your application like this:--><p>例如，<code>NSMutableDictionary</code> 在跨并发域传递时并不安全，所以它没办法安全得遵循 <code>Sendable</code>。上面的 struct 允许你（作为应用程序的程序员）像这样写一个 actor 的 API：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">MyAppActor</span> &#123;</span><br><span class="line">  <span class="comment">// The caller *promises* that it won&#x27;t use the transferred object.</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">doStuff</span>(<span class="params">dict</span>: <span class="type">UnsafeTransfer</span>&lt;<span class="type">NSMutableDictionary</span>&gt;) <span class="keyword">async</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--While this isn’t particularly pretty, it is effective at getting things done on the caller side when you need to work with unaudited and unsafe code.  This can also be sugared into a parameter attribute using the recently proposed [extension to property wrappers for arguments](https://forums.swift.org/t/pitch-2-extend-property-wrappers-to-function-and-closure-parameters/40959), allowing a prettier declaration and caller-side syntax:--><p>虽然这不是特别优雅，但当你需要处理未经审计和不安全的代码时，它能有效地在调用者一方完成工作。这也可以使用最近提出的<a href="https://forums.swift.org/t/pitch-2-extend-property-wrappers-to-function-and-closure-parameters/40959">将 propertyWrapper 拓展到函数参数</a>，变成一个参数属性，允许一种更优雅的声明和调用方语法：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">MyAppActor</span> &#123;</span><br><span class="line">  <span class="comment">// The caller *promises* that it won&#x27;t use the transferred object.</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">doStuff</span>(<span class="meta">@UnsafeTransfer</span> <span class="params">dict</span>: <span class="type">NSMutableDictionary</span>) <span class="keyword">async</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Objective-C-框架支持"><a href="#Objective-C-框架支持" class="headerlink" title="Objective-C 框架支持"></a>Objective-C 框架支持</h3><!--**NOTE**: This section is NOT considered part of the proposal — it is included just to illustrate aspects of the design.--><p><strong>注意</strong>。本节不属于是提案的一部分 - 它只是为了说明设计的各个方面。</p><!--Objective-C has established patterns that would make sense to pull into this framework en-masse, e.g. the [`NSCopying` protocol](https://developer.apple.com/documentation/foundation/nscopying) is one important and widely adopted protocol that should be onboarded into this framework.--><p>Objective-C 已经建立了一些模式，可以合理地融入到这个框架里，例如，<a href="https://developer.apple.com/documentation/foundation/nscopying"><code>NSCopying</code> 协议</a>是一个重要的、被广泛采用的协议，应该被纳入这个框架。</p><!--General consensus is that it is important to make copies explicit in the model, so we can implement an `NSCopied` helper like so:--><p>一般的共识是，在模型中明确复制是很重要的，所以我们可以像这样实现一个 <code>NSCopied</code> helper：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@propertyWrapper</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">NSCopied</span>&lt;<span class="type">Wrapped</span>: <span class="type">NSCopying</span>&gt;: @unchecked <span class="title class_ inherited__">Sendable</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> wrappedValue: <span class="type">Wrapped</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">init</span>(<span class="params">wrappedValue</span>: <span class="type">Wrapped</span>) &#123;</span><br><span class="line">    <span class="keyword">self</span>.wrappedValue <span class="operator">=</span> wrappedValue.copy() <span class="keyword">as!</span> <span class="type">Wrapped</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This would allow individual arguments and results of actor methods to opt-into a copy like this:--><p>这将允许 actor 方法的个别参数和结果选择使用一个单独的副本：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">MyAppActor</span> &#123;</span><br><span class="line">  <span class="comment">// The string is implicitly copied each time you invoke this.</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">lookup</span>(<span class="meta">@NSCopied</span> <span class="params">name</span>: <span class="type">NSString</span>) -&gt; <span class="type">Int</span> <span class="keyword">async</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--One random note: the Objective-C static type system is not very helpful to us with immutability here: statically typed `NSString`’s may actually be dynamically `NSMutableString`’s due to their subclass relationships.  Because of this, it isn’t safe to assume that values of `NSString` type are dynamically immutable — they should be implemented to invoke the `copy()` method.--><p>一个粗略的解释：Objective-C 的静态类型系统在这里对我们的 immutability 帮助不大：静态类型的 <code>NSString</code> 由于可以被继承，所以实际上可能是动态的 <code>NSMutableString</code>。正因为如此，我们无法假设 <code>NSString</code> 类型的值是动态不可变的 – 它们应该在实现里调用 <code>copy()</code> 方法。</p><h3 id="和-actor-self-与-Sendable-闭包的交互"><a href="#和-actor-self-与-Sendable-闭包的交互" class="headerlink" title="和 actor self 与 @Sendable 闭包的交互"></a>和 actor self 与 <code>@Sendable</code> 闭包的交互</h3><!--Actors are a proposal that is conceptually layered on top of this one, but it is important to be aware of the actor design to make sure that this proposal addresses its needs.  As described above, actor method sends across concurrency boundaries naturally require that arguments and results conform to `Sendable`, and thus implicitly require that closures passed across such boundaries are `@Sendable`.--><p>Actor 是基于这个提案之上提出的，但了解 actor 的设计也很重，以确保本提案能满足其需求。如上所述，actor 方法跨越并发边界的发送，要求参数和结果遵循 <code>Sendable</code>，因此隐含着一个要求，跨越这种边界传递的闭包必须是 <code>@Sendable</code> 的。</p><!--One additional detail that needs to be addressed is “when is something a cross actor call?”.  For example, we would like these calls to be synchronous and not require an await:--><p>还有一个需要解决的细节是”什么时候算是跨 actor 的调用？”。例如，我们希望这些调用是同步的，不需要 await：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">SomeActor</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">oneSyncFunction</span>(<span class="params">x</span>: <span class="type">Int</span>) &#123;<span class="operator">...</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">otherSyncFunction</span>() &#123;</span><br><span class="line">    <span class="comment">// No await needed: stays in concurrency domain of self actor.</span></span><br><span class="line">    <span class="keyword">self</span>.oneSyncFunction(x: <span class="number">42</span>)</span><br><span class="line">    oneSyncFunction(x: <span class="number">7</span>)    <span class="comment">// Implicit self is fine.</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--However, we also need to consider the case when ‘self’ is captured into a closure within an actor method.  For example:--><p>然而，我们也需要考虑当 <code>self</code> 被捕获到 actor 方法中的闭包时的情况。比如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">SomeActor</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">thing</span>(<span class="params">arr</span>: [<span class="type">Int</span>]) &#123;</span><br><span class="line">    <span class="comment">// This should obviously be allowed!</span></span><br><span class="line">    arr.forEach &#123; <span class="keyword">self</span>.oneSyncFunction(x: <span class="variable">$0</span>) &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Error: await required because it hops concurrency domains.</span></span><br><span class="line">    arr.parallelMap &#123; <span class="keyword">self</span>.oneSyncFunction(x: <span class="variable">$0</span>) &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Is this ok?</span></span><br><span class="line">    someHigherOrderFunction &#123;</span><br><span class="line">      <span class="keyword">self</span>.oneSyncFunction(x: <span class="number">7</span>)  <span class="comment">// ok or not?</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--We need the compiler to know whether there is a possible concurrency domain hop or not — if so, an await is required.  Fortunately, this works out through straight-forward composition of the basic type system rules above: It is perfectly safe to use actor `self` in a non-`@Sendable` closure in an actor method, but using it in a `@Sendable` closure is treated as being from a different concurrency domain, and thus requires an `await`.--><p>我们需要编译器知道是否有一个可能的并发域跳转 – 如果有，就需要一个 await。幸运的是，这可以通过上述基本类型系统规则的直接组合来实现。在 actor 方法的非 <code>@Sendable</code> 闭包中使用 actor <code>self</code> 是完全安全的，但是在 <code>@Sendable</code> 闭包中使用它将被视为来自不同的并发域，因此需要一个 <code>await</code>。</p><h3 id="Marker-协议作为一个自定义注解"><a href="#Marker-协议作为一个自定义注解" class="headerlink" title="Marker 协议作为一个自定义注解"></a>Marker 协议作为一个自定义注解</h3><!--The marker protocol `Sendable` and the function attribute `@Sendable` are intentionally given the same name. There is a potential future direction here where `@Sendable` could move from a special attribute recognized by the compiler (as in this proposal), to having marker protocols like `Sendable` be custom attributes like [property wrappers](https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md) and [result builders](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md). Such a change would have very little effect on existing code that uses `@Sendable` so long as users don't declare their own `Sendable` type that shadows the one from the standard library. However, it would make `@Sendable` less special and allow other marker protocols to be used similarly.--><p>marker 协议 <code>Sendable</code> 和函数注解 <code>@Sendable</code> 被故意赋予相同的名字。这里有一个潜在的未来方向，即 <code>@Sendable</code> 可以从一个被编译器识别的特殊注解（如这个提议），类似 <code>Sendable</code> 这样使用一个通用的 marker 协议机制，成为自定义属性，如 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md">propertyWrapper</a> 和 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md">resultBuilder</a>。这样的改变对使用 <code>@Sendable</code> 现有的代码影响很小，只要用户不声明他们自己的 <code>Sendable</code> 类型，不与标准库中的类型同名。然而，它将使 <code>@Sendable</code> 不再特殊，并允许其他 marker 协议以类似的方式使用。</p><h2 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a>代码兼容性</h2><!--This is almost completely source compatible with existing code bases. The introduction of the `Sendable` marker protocol  and `@Sendable` functions are additive features that have no impact when not used and therefore do not affect existing code.There are a few new restrictions that could cause source breakage in exotic cases:*   The change to keypath literals subscripts will break exotic keypaths that are indexed with non-standard types.*   `Error` and `CodingKey` inherit from `Sendable` and thus require that custom errors and keys conform to `Sendable`.Because of these changes, the new restrictions will only be enforced in Swift 6 mode, but will be warnings for Swift 5 and earlier.--><p>这几乎与现有的代码库完全代码兼容。引入 <code>Sendable</code> marker 协议和 <code>@Sendable</code> 函数注解是附加功能，不使用时没有影响，因此不会影响现有代码。</p><p>但这里有一些新的限制，在特殊情况下可能会导致代码兼容性破坏：</p><ul><li>对 keypath 字面量下标的改变将破坏用非标准类型索引的外部 keypath。</li><li><code>Error</code> 和 <code>CodingKey</code> 将添加 <code>Sendable</code> 的继承，因此要求自定义 Error 和 CodingKey 符合 <code>Sendable</code>。</li></ul><p>由于这些修改，新的限制将只在 Swift 6 模式下执行，但对于 Swift 5 和更早的版本将是警告。</p><h2 id="对-API-resilience-的影响"><a href="#对-API-resilience-的影响" class="headerlink" title="对 API resilience 的影响"></a>对 API resilience 的影响</h2><!--This proposal has no effect on API resilience!--><p>这项建议对 API 的弹性没有任何影响!</p><h2 id="其它备选方案"><a href="#其它备选方案" class="headerlink" title="其它备选方案"></a>其它备选方案</h2><!--There are several alternatives that make sense to discuss w.r.t. this proposal.  Here we capture some of the bigger ones.--><p>在讨论这个提案时，有几个备选方案也是很有意义的。在这里，我们尝试讨论一些较大的问题。</p><h3 id="奇异的类型系统功能"><a href="#奇异的类型系统功能" class="headerlink" title="奇异的类型系统功能"></a>奇异的类型系统功能</h3><!--The [Swift Concurrency Roadmap](https://forums.swift.org/t/swift-concurrency-roadmap/41611) mentions that a future iteration of the feature set could introduce new type system features like “`mutableIfUnique`” classes, and it is easy to imagine that move semantics and unique ownership could get introduced into Swift someday.--><p><a href="https://forums.swift.org/t/swift-concurrency-roadmap/41611">Swift Concurrency Roadmap</a> 提到，未来的功能集迭代可能会引入新的类型系统特性，如 “<code>mutableIfUnique</code>“ 类，而且很容易想象，move 语义和唯一所有权有一天会被引入 Swift。</p><!--While it is difficult to understand the detailed interaction without knowing the full specification of future proposals, we believe that the checking machinery that enforces `Sendable` checking is simple and composable.  It should work with any types that are safe to pass across concurrency boundaries.--><p>虽然在不了解未来提案完整规范的情况下，很难理解详细的交互逻辑，但我们相信执行 <code>Sendable</code> 检查的检查机制是简单和可组合的。它应该适用于任何可以安全跨越并发边界的类型。</p><h3 id="支持一种显式的-copy-hook"><a href="#支持一种显式的-copy-hook" class="headerlink" title="支持一种显式的 copy hook"></a>支持一种显式的 copy hook</h3><!--The [first revision of this proposal](https://docs.google.com/document/d/1OMHZKWq2dego5mXQtWt1fm-yMca2qeOdCl8YlBG1uwg/edit#) allowed types to define custom behavior when they are sent across concurrency domains, through the implementation of an `unsafeSend` protocol requirement.  This increased the complexity of the proposal, admitted undesired functionality (explicitly implemented copy behavior), made the recursive aggregate case more expensive, and would result in larger code size.--><p>本提案的<a href="https://docs.google.com/document/d/1OMHZKWq2dego5mXQtWt1fm-yMca2qeOdCl8YlBG1uwg/edit#">第一次修订</a>允许类型在跨并发域发送时自定义行为，通过实现 <code>unsafeSend</code> 协议要求。这增加了提案的复杂性，添加了不必要的功能（明确实现的复制行为），使递归聚合的情况消耗更多性能，并且会导致更大的代码二进制文件。</p><h2 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h2><!--This proposal defines a very simple approach for defining types that are safe to transfer across concurrency domains.  It requires minimal compiler/language support that is consistent with existing Swift features, is extensible by users, works with legacy code bases, and provides a simple model that we can feel good about even 20 years from now.--><p>这项提案引入了一种非常简单的方式，用于定义可以安全地跨并发域传输的类型。它需要的编译器&#x2F;语言支持很少，与现有的 Swift 功能一致，可由用户扩展，与传统的代码库一起使用，并提供了一个简单的模型，即使在 20 年后我们也会觉得很棒。</p><!--Because the feature is mostly a library feature that builds on existing language support, it is easy to define wrapper types that extend it for domain specific concerns (along the lines of the `NSCopied` example above), and retroactive conformance makes it easy for users to work with older libraries that haven’t been updated to know about the Swift Concurrency model yet.--><p>因为该功能主要是建立在现有语言支持基础上的库功能，所以很容易定义包装类型，为特定领域的关注点进行扩展（按照上面 <code>NSCopied</code> 的例子），跳过遵循检查使用户很容易与尚未更新以了解 Swift 并发模型的旧库合作。</p><h2 id="修订历史"><a href="#修订历史" class="headerlink" title="修订历史"></a>修订历史</h2><ul><li>Changes from the second review:<ul><li>Renamed <code>@sendable</code> to <code>@Sendable</code>, per review feedback and Core Team decision.</li><li>Add a future direction on marker protocols as custom attributes.</li><li>Removed “Swift Concurrency 1.0” and “2.0” discussion in Alternatives Considered.</li></ul></li><li>Changes from the first review<ul><li>Renamed <code>ConcurrentValue</code> to <code>Sendable</code> and <code>@concurrent</code> to <code>@sendable</code>.</li><li>Replaced <code>UnsafeConcurrentValue</code> with <code>@unchecked Sendable</code> conformances.</li><li>Add implicit conformance to <code>Sendable</code> for non-public, non-frozen <code>struct</code> and <code>enum</code> types.</li></ul></li></ul>]]>
    </content>
    <id>https://kemchenj.github.io/2022-01-07/</id>
    <link href="https://kemchenj.github.io/2022-01-07/"/>
    <published>2022-01-06T16:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md">SE-0302 Sendable and @Sendable closures</a></p>
</blockquote>
<ul>
<li>Proposal: <a href="0302-concurrent-value-and-concurrent-closures.md">SE-0302</a></li>
<li>Authors: <a href="https://github.com/lattner">Chris Lattner</a>, <a href="https://github.com/douggregor">Doug Gregor</a></li>
<li>Review Manager: <a href="https://github.com/rjmccall">John McCall</a></li>
<li>Status: <strong>Accepted (2021-03-16)</strong></li>
<li>Implementation: <a href="https://github.com/apple/swift/pull/35264">apple&#x2F;swift#35264</a></li>
<li>Major Contributors: Dave Abrahams, Paul Cantrell, Matthew Johnson, John McCall</li>
<li>Review: (<a href="https://forums.swift.org/t/se-0302-Sendable-and-concurrent-closures/44919">first review</a>) (<a href="https://forums.swift.org/t/returned-for-revision-se-0302-concurrentvalue-and-concurrent-closures/45251">revision announcement</a>) (<a href="https://forums.swift.org/t/se-0302-second-review-sendable-and-sendable-closures/45253">second review</a>) (<a href="https://forums.swift.org/t/accepted-se-0302-sendable-and-sendable-closures/45786">acceptance</a>)</li>
</ul>
<!--
## Contents

  * [Introduction](#introduction)
  * [Motivation](#motivation)
     * [<g-emoji class="g-emoji" alias="sparkling_heart" fallback-src="https://github.githubassets.com/images/icons/emoji/unicode/1f496.png">💖</g-emoji> Swift   Value Semantics](#-swift--value-semantics)
     * [Value Semantic Composition](#value-semantic-composition)
     * [Higher Order Functional Programming](#higher-order-functional-programming)
     * [Immutable Classes](#immutable-classes)
     * [Internally Synchronized Reference Types](#internally-synchronized-reference-types)
     * [“Transferring” Objects Between Concurrency Domains](#transferring-objects-between-concurrency-domains)
     * [Deep Copying Classes](#deep-copying-classes)
     * [Motivation Conclusion](#motivation-conclusion)
  * [Proposed Solution   Detailed Design](#proposed-solution--detailed-design)
     * [Marker Protocols](#marker-protocols)
     * [Sendable Protocol](#sendable-protocol)
        * [Tuple conformance to Sendable](#tuple-conformance-to-sendable)
        * [Metatype conformance to Sendable](#metatype-conformance-to-sendable)
        * [Sendable conformance checking for structs and enums](#sendable-conformance-checking-for-structs-and-enums)
        * [Implicit struct/enum conformance to Sendable](#implicit-structenum-conformance-to-sendable)
        * [Sendable conformance checking for classes](#sendable-conformance-checking-for-classes)
        * [Actor types](#actor-types)
        * [Key path literals](#key-path-literals)
     * [New @Sendable attribute for functions](#new-sendable-attribute-for-functions)
        * [Inference of @Sendable for Closure Expressions](#inference-of-sendable-for-closure-expressions)
     * [Thrown errors](#thrown-errors)
     * [Adoption of Sendable by Standard Library Types](#adoption-of-sendable-by-standard-library-types)
     * [Support for Imported C / Objective-C APIs](#support-for-imported-c--objective-c-apis)
  * [Future Work / Follow-on Projects](#future-work--follow-on-projects)
     * [Adaptor Types for Legacy Codebases](#adaptor-types-for-legacy-codebases)
     * [Objective-C Framework Support](#objective-c-framework-support)
     * [Interaction of Actor self and @Sendable closures](#interaction-of-actor-self-and-sendable-closures)
     * [Marker protocols as custom attributes](#marker-protocols-as-custom-attributes)
  * [Source Compatibility](#source-compatibility)
  * [Effect on API resilience](#effect-on-api-resilience)
  * [Alternatives Considered](#alternatives-considered)
     * [Exotic Type System Features](#exotic-type-system-features)
     * [Support an explicit copy hook](#support-an-explicit-copy-hook)
  * [Conclusion](#conclusion)
  * [Revision history](#revision-history)
-->

<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--
A key goal of the Swift Concurrency effort is to “provide a mechanism for isolating state in concurrent programs to eliminate data races.”  Such a mechanism will be a major progression for widely used programming languages — most of them provide concurrent programming abstractions in a way that subjects programmers to a wide range of bugs, including race conditions, deadlocks and other problems.
-->

<p>Swift Concurrency 的其中一个关键目标就是“提供一种机制来隔离并发程序中的状态，以消除 data races”。这样的机制将会是通用编程语言的一次重大进步 – 大多数语言提供的并发编程抽象会使程序员面临范围宽广的 bug，包括 race conditions，死锁和其它问题。</p>
<!--
This proposal describes an approach to address one of the challenging problems in this space — how to type check value passing between structured concurrency constructs and actors messages. As such, this is a unifying theory that provides some of the underlying type system mechanics that make them both safe and work well together.
-->

<p>这个提案里描述了一种方式，去解决这个领域面临的其中一个问题 – 如何对结构化并发和 Actor 消息传递的值进行类型检查。因此，这是一个统一的理论，它提供了一些基本的类型系统机制，使它们既保障安全又能很好地协同工作。</p>
<!--
This implementation approach involves a marker protocol named `Sendable`, as well as a `@Sendable` attribute that may be applied to functions.
-->

<p>这种实现方式会提供一个名为 <code>Sendable</code> 的 marker 协议，以及一个可应用于函数的 <code>@Sendable</code> 注解。</p>]]>
    </summary>
    <title>【译】SE-0302 Sendable 和 @Sendable 闭包</title>
    <updated>2026-09-02T11:08:19.194Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<blockquote><p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md">SE-0306 Actors</a></p></blockquote><ul><li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md">SE-0306</a></li><li>Authors: <a href="https://github.com/rjmccall">John McCall</a>, <a href="https://github.com/DougGregor">Doug Gregor</a>, <a href="https://github.com/ktoso">Konrad Malawski</a>, <a href="https://github.com/lattner">Chris Lattner</a></li><li>Review Manager: <a href="https://github.com/jckarter">Joe Groff</a></li><li>Status: <strong>Implemented (Swift 5.5)</strong></li><li>Decision Notes: <a href="https://forums.swift.org/t/accepted-with-modification-se-0306-actors/47662">Acceptance</a>, <a href="https://forums.swift.org/t/se-0306-actors/45734">First Review</a>, <a href="https://forums.swift.org/t/se-0306-second-review-actors/47291">Second Review</a></li><li>Implementation: Partially available in <a href="https://swift.org/download/#snapshots">recent <code>main</code> snapshots</a> behind the flag <code>-Xfrontend -enable-experimental-concurrency</code></li></ul><!--## Table of Contents* [Introduction](#introduction)* [Proposed solution](#proposed-solution)   * [Actors](#actors-1)   * [Actor isolation](#actor-isolation)   * [Cross-actor references and Sendable types](#cross-actor-references-and-sendable-types)   * [Closures](#closures)   * [Actor reentrancy](#actor-reentrancy)      * ["Interleaving" execution with reentrant actors](#interleaving-execution-with-reentrant-actors)      * [Deadlocks with non-reentrant actors](#deadlocks-with-non-reentrant-actors)      * [Unnecessary blocking with non-reentrant actors](#unnecessary-blocking-with-non-reentrant-actors)      * [Existing practice](#existing-practice)      * [Reentrancy Summary](#reentrancy-summary)   * [Protocol conformances](#protocol-conformances)* [Detailed design](#detailed-design)   * [Actors](#actors-2)   * [Actor isolation checking](#actor-isolation-checking)      * [References and actor isolation](#references-and-actor-isolation)      * [Protocol conformance](#protocol-conformance)   * [Partial applications](#partial-applications)   * [Key paths](#key-paths)   * [inout parameters](#inout-parameters)   * [Actor interoperability with Objective-C](#actor-interoperability-with-objective-c)* [Source compatibility](#source-compatibility)* [Effect on ABI stability](#effect-on-abi-stability)* [Effect on API resilience](#effect-on-api-resilience)* [Future Directions](#future-directions)   * [Non-reentrancy](#non-reentrancy)   * [Task-chain reentrancy](#task-chain-reentrancy)* [Alternatives considered](#alternatives-considered)    * [Actor inheritance](#actor-inheritance)   * [Cross-actor lets](#cross-actor-lets)* [Revision history](#revision-history)--><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--The Swift concurrency model intends to provide a safe programming model that statically detects [data races](https://en.wikipedia.org/wiki/Race_condition#Data_race) and other common concurrency bugs. The [Structured Concurrency][sc] proposal introduces a way to define concurrent tasks and provides data-race safety for functions and closures. This model is suitable for a number of common design patterns, including things like parallel maps and concurrent callback patterns, but is limited to working with state that is captured by closures.--><p>Swift 并发模型计划提供一个安全的编程模型，静态地检测 <a href="https://en.wikipedia.org/wiki/Race_condition#Data_race">data-races</a> 和其他常见的并发错误。<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发</a>提案引入了一种定义并发任务的方法，并为函数和闭包提供了安全的 data-race。这个模型适用于许多常见的设计模式，包括像 parallel maps 和并发回调模式，但只限于处理由闭包捕获的状态。</p><!--Swift includes classes, which provide a mechanism for declaring mutable state that is shared across the program. Classes, however, are notoriously difficult to correctly use within concurrent programs, requiring error-prone manual synchronization to avoid data races. We want to provide the ability to use shared mutable state while still providing static detection of data races and other common concurrency bugs.--><p>Swift 包含了 class，它提供了一种机制来声明整个程序共享的 mutable 状态。然而，class 在并发程序中难以正确使用，需要手动同步来避免 data race，很容易出错。我们希望提供这么一种功能，在使用共享的 mutable 状态的同时，仍然提供对 data race 和其他常见并发 bug 的静态检测。</p><!--The [actor model](https://en.wikipedia.org/wiki/Actor_model) defines entities called *actors* that are perfect for this task. Actors allow you as a programmer to declare that a bag of state is held within a concurrency domain and then define multiple operations that act upon it. Each actor protects its own data through *data isolation*, ensuring that only a single thread will access that data at a given time, even when many clients are concurrently making requests of the actor. As part of the Swift Concurrency Model, actors provide the same race and memory safety properties as structured concurrency, but provide the familiar abstraction and reuse features that other explicitly declared types in Swift enjoy.--><p><a href="https://en.wikipedia.org/wiki/Actor_model">Actor 模型</a>定义了称为 <em>actors</em> 的实体，这些实体非常适合这项任务。Actor 允许你，作为一个程序员，声明一系列的状态，这些状态由一个 concurrency domain 持有，并且可以定义多个对其的操作。每个 actor 通过 <em>data isolation</em> 来保护自己的数据，确保在给定时间内只有一个线程访问它的数据，即使许多用户同时向 actor 发出请求。作为 Swift 并发模型的一部分，actor 提供了与结构化并发相同的 race 和内存安全属性，但也提供了 Swift 中其他显式声明的类型所享有的熟悉的抽象和复用的特性。</p><span id="more"></span><p>Swift-evolution threads:</p><ul><li><a href="https://forums.swift.org/t/concurrency-actors-actor-isolation/41613">Pitch #1</a>,</li><li><a href="https://forums.swift.org/t/pitch-2-actors/44094">Pitch #2</a>,</li><li><a href="https://forums.swift.org/t/pitch-3-actors/44470">Pitch #3</a>,</li><li><a href="https://forums.swift.org/t/pitch-4-actors/45215">Pitch #4</a>,</li><li><a href="https://forums.swift.org/t/pitch-4-actors/45215/36">Pitch #5</a>,</li><li><a href="https://forums.swift.org/t/pitch-6-actors/45519">Pitch #6</a>,</li><li><a href="https://forums.swift.org/t/se-0306-actors/45734">Review #1</a></li></ul><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><h3 id="Actors"><a href="#Actors" class="headerlink" title="Actors"></a>Actors</h3><!--This proposal introduces *actors* into Swift. An actor is a reference type that protects access to its mutable state, and is introduced with the keyword `actor`:--><p>这项提案将引入 <em>actors</em>。Actor 是一种引用类型，可以保护它的 mutable state 的访问，通过关键字 <code>actor</code> 引入：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> accountNumber: <span class="type">Int</span></span><br><span class="line">  <span class="keyword">var</span> balance: <span class="type">Double</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">init</span>(<span class="params">accountNumber</span>: <span class="type">Int</span>, <span class="params">initialDeposit</span>: <span class="type">Double</span>) &#123;</span><br><span class="line">    <span class="keyword">self</span>.accountNumber <span class="operator">=</span> accountNumber</span><br><span class="line">    <span class="keyword">self</span>.balance <span class="operator">=</span> initialDeposit</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Like other Swift types, actors can have initializers, methods, properties, and subscripts. They can be extended and conform to protocols, be generic, and be used with generics.--><p>就像其它的 Swift 类型，<code>actor</code> 可以有构造器、方法、属性和下标。它们可以被扩展并遵循协议，可以是泛型，也可以与泛型一起使用。</p><!--The primary difference is that actors protect their state from data races. This is enforced statically by the Swift compiler through a set of limitations on the way in which actors and their instance members can be used, collectively called *actor isolation*.   --><p>最主要的区别是，actor 会保护其状态不受 data-race 影响。这一点会由 Swift 编译器静态地强制执行，通过对 actor 及其实例成员的使用方式进行一系列限制，这些限制统称为 <em>actor isolation</em>。  </p><h3 id="Actor-隔离"><a href="#Actor-隔离" class="headerlink" title="Actor 隔离"></a>Actor 隔离</h3><!--Actor isolation is how actors protect their mutable state. For actors, the primary mechanism for this protection is by only allowing their stored instance properties to be accessed directly on `self`. For example, here is a method that attempts to transfer money from one account to another:--><p>Actor isolation 是 actor 保护其可变状态的方式。对于 actor 来说，这种保护的主要机制是只允许实例的存储属性通过 <code>self</code> 访问。例如，这里有一个方法，试图将钱从一个账户转到另一个账户：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">enum</span> <span class="title class_">BankError</span>: <span class="title class_ inherited__">Error</span> &#123;</span><br><span class="line">    <span class="keyword">case</span> insufficientFunds</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">transfer</span>(<span class="params">amount</span>: <span class="type">Double</span>, <span class="params">to</span> <span class="params">other</span>: <span class="type">BankAccount</span>) <span class="keyword">throws</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> amount <span class="operator">&gt;</span> balance &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="type">BankError</span>.insufficientFunds</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Transferring <span class="subst">\(amount)</span> from <span class="subst">\(accountNumber)</span> to <span class="subst">\(other.accountNumber)</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    balance <span class="operator">=</span> balance <span class="operator">-</span> amount</span><br><span class="line">    other.balance <span class="operator">=</span> other.balance <span class="operator">+</span> amount  <span class="comment">// error: actor-isolated property &#x27;balance&#x27; can only be referenced on &#x27;self&#x27;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--If `BankAccount` were a class, the `transfer(amount:to:)` method would be well-formed, but would be subject to data races in concurrent code without an external locking mechanism. --><p>如果 <code>BankAccount</code> 是一个 class，<code>transfer(amount:to:)</code> 方法就是定义正确的，但在没有外部加锁的并发代码中会出现 data race。</p><!--With actors, the attempt to reference `other.balance` triggers a compiler error, because `balance` may only be referenced on `self`. The error message notes that `balance` is *actor-isolated*, meaning that it can only be accessed directly from within the specific actor it is tied to or "isolated by". In this case, it's the instance of `BankAccount` referenced by `self`. All declarations on an instance of an actor, including stored and computed instance properties (like `balance`), instance methods (like `transfer(amount:to:)`), and instance subscripts, are all actor-isolated by default. Actor-isolated declarations can freely refer to other actor-isolated declarations on the same actor instance (on `self`). Any declaration that is not actor-isolated is *non-isolated* and cannot synchronously access any actor-isolated declaration.--><p>使用 actor 时，试图访问 <code>other.balance</code> 会引发编译错误，因为 <code>balance</code> 只能通过 <code>self</code> 访问。错误信息指出 <code>balance</code> 是隔离在 actor 里的，意味着它只能从它被绑定或”隔离”的特定 actor 中直接访问。在这个例子中，它是由 <code>self</code> 引用的 <code>BankAccount</code> 的实例。所有关于 actor 实例的声明，包括存储和计算的实例属性（如 <code>balance</code>）、实例方法（如<code>transfer(amount:to:)</code>）和实例下标，默认都是被 actor 隔离的。Actor 隔离的声明可以自由地引用同一 actor 实例上的其他被 actor 隔离的声明（通过 <code>self</code>）。任何没有被 actor 隔离的的声明都是<em>不被隔离</em>的，不能同步访问任何被 actor 隔离的声明。</p><!--A reference to an actor-isolated declaration from outside that actor is called a *cross-actor reference*. Such references are permissible in one of two ways. First, a cross-actor reference to immutable state is allowed from anywhere in the same module as the actor is defined because, once initialized, that state can never be modified (either from inside the actor or outside it), so there are no data races by definition. The reference to `other.accountNumber` is allowed based on this rule, because `accountNumber` is declared via a `let` and has value-semantic type `Int`.--><p>在一个 actor 之外对该 actor 隔离的声明的引用被称为<em>跨 actor 引用</em>，这种引用有两种方式是合法的。</p><p>第一，对不可变的状态的跨 actor 引用是合法的，因为一旦初始化，该状态就不能被修改（无论是从 actor 内部还是外部），所以从定义上来说没有数据竞争。根据这个规则，对 <code>other.accountNumber</code> 的引用是允许的，因为 <code>accountNumber</code> 是通过一个 <code>let</code> 声明的，并且具有值语义类型 <code>Int</code>。</p><!--The second form of permissible cross-actor reference is one that is performed with an asynchronous function invocation. Such asynchronous function invocations are turned into "messages" requesting that the actor execute the corresponding task when it can safely do so. These messages are stored in the actor's "mailbox", and the caller initiating the asynchronous function invocation may be suspended until the actor is able to process the corresponding message in its mailbox. An actor processes the messages in its mailbox sequentially, so that a given actor will never have two concurrently-executing tasks running actor-isolated code. This ensures that there are no data races on actor-isolated mutable state, because there is no concurrency in any code that can access actor-isolated state. For example, if we wanted to make a deposit to a given bank account `account`, we could make a call to a method `deposit(amount:)` on another actor, and that call would become a message placed in the actor's mailbox and the caller would suspend. When that actor processes messages, it will eventually process the message corresponding to the deposit, executing that call within the actor's isolation domain when no other code is executing in that actor's isolation domain.--><p>第二种合法的跨 actor 引用的形式是用异步函数调用。这种异步函数调用会被转化为”消息”，actor 会在可以安全地执行的时候，执行相应的任务。这些消息会存储在 actor 的”邮箱”中，启动异步函数调用的调用者可能被暂停，直到 actor 能够处理其邮箱中的相应消息。Actor 按顺序处理其邮箱中的消息，因此，一个给定的 actor 将永远不会有两个同时执行的任务，运行 actor 隔离的代码。这确保了在 actor 隔离的可变状态上不会有数据竞赛，因为在任何可以访问 actor 隔离的状态的代码中都没有并发性。例如，如果我们想给一个给定的银行账户 <code>account</code> 存款，我们可以调用另一个 actor 上的方法 <code>deposit(amount:)</code>，这个调用将成为一个放在该 actor 邮箱中的消息，调用者将暂停。当该 actor 处理消息时，它最终会处理与存款相对应的消息，当没有其他代码在该 actor 的隔离域中执行时，就在该 actor 的隔离域中执行该调用。</p><!--> **Implementation note**: At an implementation level, the messages are partial tasks (described by the [Structured Concurrency][sc] proposal) for the asynchronous call, and each actor instance contains its own serial executor (also in the [Structured Concurrency][sc] proposal). The serial executor is responsible for running the partial tasks sequentially. This is conceptually similar to a serial [`DispatchQueue`](https://developer.apple.com/documentation/dispatch/dispatchqueue), but the actual implementation in the actor runtime uses a lighter-weight implementation that takes advantage of Swift's `async` functions.--><blockquote><p><strong>实现说明</strong>。在实现层面上，消息是异步调用的 partial task（由<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发</a>提案描述），每个 actor 实例包含自己的 serial executor（也在<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发</a>提案中）。Serial executor 负责按顺序运行部分任务。这在概念上类似于一个串行的 <a href="https://developer.apple.com/documentation/dispatch/dispatchqueue"><code>DispatchQueue</code></a>，但在 actor 运行时的实际实现中使用了一个更轻量级的实现，利用了 Swift 的 <code>async</code> 函数。</p></blockquote><!--Compile-time actor-isolation checking determines which references to actor-isolated declarations are cross-actor references, and ensures that such references use one of the two permissible mechanisms described above. This ensures that code outside of the actor does not interfere with the actor's mutable state.--><p>编译时 actor-isolation 检查会确定哪些对被 actor 隔离的声明引用是跨 actor 的引用，并确保这种引用使用上述两种允许的机制之一。这确保了 actor 之外的代码不会干扰 actor 的 mutable 状态。</p><!--Based on the above, we can implement a correct version of `transfer(amount:to:)` that is asynchronous:--><p>基于上述，我们可以实现一个正确的 <code>transfer(amount:to:)</code> 版本，它是异步的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">transfer</span>(<span class="params">amount</span>: <span class="type">Double</span>, <span class="params">to</span> <span class="params">other</span>: <span class="type">BankAccount</span>) <span class="keyword">async</span> <span class="keyword">throws</span> &#123;</span><br><span class="line">    <span class="built_in">assert</span>(amount <span class="operator">&gt;</span> <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> amount <span class="operator">&gt;</span> balance &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="type">BankError</span>.insufficientFunds</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;Transferring <span class="subst">\(amount)</span> from <span class="subst">\(accountNumber)</span> to <span class="subst">\(other.accountNumber)</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 安全：这是当前唯一一个访问被 actor 隔离的状态的操作，并且</span></span><br><span class="line">    <span class="comment">// 在我们检查是否有足够的资金之后，执行到这里的逻辑之前，中间没有</span></span><br><span class="line">    <span class="comment">// 任何暂停点。</span></span><br><span class="line">    balance <span class="operator">=</span> balance <span class="operator">-</span> amount</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 安全：存款的操作会被放到 `other` actor 的邮箱里；</span></span><br><span class="line">    <span class="comment">// actor 会从邮箱里取出这个操作并且执行它，other 的</span></span><br><span class="line">    <span class="comment">// 账户余额将会被更新。</span></span><br><span class="line">    <span class="keyword">await</span> other.deposit(amount: amount)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The `deposit(amount:)` operation needs involve the state of a different actor, so it must be invoked asynchronously. This method could itself be implemented as `async`:--><p><code>deposit(amount:)</code> 操作需要涉及不同 actor 的状态，所以必须异步调用。这个方法本身可以被实现为 <code>async</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">deposit</span>(<span class="params">amount</span>: <span class="type">Double</span>) <span class="keyword">async</span> &#123;</span><br><span class="line">    <span class="built_in">assert</span>(amount <span class="operator">&gt;=</span> <span class="number">0</span>)</span><br><span class="line">    balance <span class="operator">=</span> balance <span class="operator">+</span> amount</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--However, this method doesn't really need to be `async`: it makes no asynchronous calls (note the lack of `await`). Therefore, it would be better defined as a synchronous function:--><p>然而，这个方法其实并不需要是 <code>async</code>：它没有进行异步调用（注意这里没有 <code>await</code>）。因此，它最好被定义为一个同步函数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">deposit</span>(<span class="params">amount</span>: <span class="type">Double</span>) &#123;</span><br><span class="line">    <span class="built_in">assert</span>(amount <span class="operator">&gt;=</span> <span class="number">0</span>)</span><br><span class="line">    balance <span class="operator">=</span> balance <span class="operator">+</span> amount</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Synchronous actor functions can be called synchronously on the actor's `self`, but cross-actor references to this method require an asynchronous call. The `transfer(amount:to:)` function calls it asynchronously (on `other`), while the following function `passGo` calls it synchronously (on the implicit `self`):--><p>同步的 actor 函数可以在 actor 的 <code>self</code> 上同步调用，但是跨 actor 引用这个方法需要异步调用。<code>transfer(amount:to:)</code> 函数异步调用它（在 <code>other</code> 上），而下面的函数 <code>passGo</code> 同步调用它（在隐式 <code>self</code> 上）。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="comment">// Pass go and collect $200</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">passGo</span>() &#123;</span><br><span class="line">    <span class="keyword">self</span>.deposit(amount: <span class="number">200.0</span>)  <span class="comment">// synchronous is okay because `self` is isolated</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Cross-actor references to an actor property are permitted as an asynchronous call so long as they are read-only accesses:--><p>只要是只读访问，就允许跨 actor 对 actor 属性的引用作为异步调用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">checkBalance</span>(<span class="params">account</span>: <span class="type">BankAccount</span>) <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="built_in">print</span>(<span class="keyword">await</span> account.balance)   <span class="comment">// okay</span></span><br><span class="line">  <span class="keyword">await</span> account.balance <span class="operator">=</span> <span class="number">1000.0</span> <span class="comment">// error: cross-actor property mutations are not permitted</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--> **Rationale**: it is possible to support cross-actor property sets. However, cross-actor `inout` operations cannot be reasonably supported because there would be an implicit suspension point between the "get" and the "set" that could introduce what would effectively be race conditions. Moreover, setting properties asynchronously may make it easier to break invariants unintentionally if, e.g., two properties need to be updated at once to maintain an invariant.--><blockquote><p><strong>理由</strong>：支持跨 actor 的属性设置是可能的。然而，不能合理地支持跨 actor 的 <code>inout</code> 操作，因为在 <code>get</code> 和 <code>set</code> 之间会有一个隐含的暂停点，可能会引入有效的竞赛条件。此外，异步设置属性可能会使其更容易无意中破坏不变性，例如，两个属性需要同时更新以保持不变性。</p></blockquote><!--From outside a module, immutable `let`s must be referenced asynchronously from outside the actor. For example:--><p>从 module 外，不可变的 <code>let</code> 必须从 actor 外异步引用。比如说：</p> <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// From another module</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">printAccount</span>(<span class="params">account</span>: <span class="type">BankAccount</span>) &#123;</span><br><span class="line">  <span class="built_in">print</span>(<span class="string">&quot;Account #<span class="subst">\(<span class="keyword">await</span> account.accountNumber)</span>&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This preserves the ability for the module that defines `BankAccount` to evolve the `let` into a `var` without breaking clients, which is a property Swift has always maintained.:--> <p>这保留了定义 <code>BankAccount</code> 的模块将 <code>let</code> 演变为 <code>var</code> 而不破坏客户端的能力，这是 Swift 一直保持的特性：</p> <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">BankAccount</span> &#123; <span class="comment">// version 2</span></span><br><span class="line">  <span class="keyword">var</span> accountNumber: <span class="type">Int</span></span><br><span class="line">  <span class="keyword">var</span> balance: <span class="type">Double</span>  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Only code within the module will need to change to account for `accountNumber` becoming a `var`; existing clients will already use asynchronous access and be unaffected.--> <p><code>accountNumber</code> 改为 <code>var</code> 之后，只有 module 内的代码需要改变；现有的客户端使用已经是异步访问，不受影响。</p><h3 id="跨-actor-引用和-Sendable-类型"><a href="#跨-actor-引用和-Sendable-类型" class="headerlink" title="跨 actor 引用和 Sendable 类型"></a>跨 actor 引用和 <code>Sendable</code> 类型</h3><!--[SE-0302][se302] introduces the `Sendable` protocol. Values of types that conform to the `Sendable` protocol are safe to share across concurrently-executing code. There are various kinds of types that work well this way: value-semantic types like `Int` and `String`, value-semantic collections of such types like `[String]` or `[Int: String]`, immutable classes, classes that perform their own synchronization internally (like a concurrent hash table), and so on.--><p><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md">SE-0302</a> 介绍了 <code>Sendable</code> 协议。符合 <code>Sendable</code> 协议的类型的值可以在并发执行的代码中安全地共享。有各种各样的类型以这种方式工作：像 <code>Int</code> 和 <code>String</code> 这样的值语义类型，像 <code>[String]</code> 或 <code>[Int: String]</code> 这样的值语义集合，immutable 的 class，内部执行自己同步的类（例如 ConcurrentHashMap），等等。</p><!--Actors protect their mutable state, so actor instances can be freely shared across concurrently-executing code, and the actor itself will internally maintain synchronization. Therefore, every actor type implicitly conforms to the `Sendable` protocol.--><p>Actor 会保护它的 mutable 状态，所以 actor 实例可以在并发执行的代码中自由共享，而且 actor  本身将在内部保持同步。因此，每个 actor 类型都隐式遵循 <code>Sendable</code> 协议。</p><!--All cross-actor references are, necessarily, working with values of types that are being shared across different concurrently-executed code. For example, let's say that our `BankAccount` includes a list of owners, where each owner is modeled by a `Person` class:--><p>所有的跨 actor 引用都，必须，与在并发执行的代码中共享的值一起使用。例如，假设我们的 <code>BankAccount</code> 包括一个所有者列表，每个所有者都建模为 <code>Person</code> 类：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> name: <span class="type">String</span></span><br><span class="line">  <span class="keyword">let</span> birthDate: <span class="type">Date</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  <span class="keyword">var</span> owners: [<span class="type">Person</span>]</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">primaryOwner</span>() -&gt; <span class="type">Person</span>? &#123; <span class="keyword">return</span> owners.first &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The `primaryOwner` function can be called asynchronously from another actor, and then the `Person` instance can be modified from anywhere:--><p><code>primaryOwner</code> 函数可以从另一个 actor 中异步调用，然后可以从任何地方修改 <code>Person</code> 实例。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="keyword">let</span> primary <span class="operator">=</span> <span class="keyword">await</span> account.primaryOwner() &#123;</span><br><span class="line">  primary.name <span class="operator">=</span> <span class="string">&quot;The Honorable &quot;</span> <span class="operator">+</span> primary.name  <span class="comment">// problem: concurrent mutation of actor-isolated state</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Even non-mutating access is problematic, because the person's `name` could be modified from within the actor at the same time as the original call is trying to access it. To prevent this potential for concurrent mutation of actor-isolated state, all cross-actor references can only involve types that conform to `Sendable`. For a cross-actor asynchronous call, the argument and result types must conform to `Sendable`. For a cross-actor reference to an immutable property, the property type must conform to `Sendable`. By insisting that all cross-actor references only use `Sendable` types, we can ensure that no references to shared mutable state flow into or out of the actor's isolation domain. The compiler will produce a diagnostic for such issues. For example, the call to `account.primaryOwner()` about would produce an error like the following:--><p>即使是 non-mutating 的访问也是会有问题的，因为在原始调用试图访问这个 Person 的时候，这个 Person 的 <code>name</code> 也可能从 actor 内部被修改。为了防止这种被 actor 隔离的状态的并发 mutate 的可能性，所有的跨 actor 引用只能涉及符合 <code>Sendable</code> 的类型。对于一个跨 actor 的异步调用，参数和返回值类型必须符合 <code>Sendable</code>。对于一个跨 actor 的不可变属性引用，属性类型必须符合 <code>Sendable</code>。通过坚持所有的跨 actor 引用只使用 <code>Sendable</code> 类型，我们可以确保对共享 mutable 状态的引用不会流入或流出 actor 的隔离域。编译器会对这类问题进行诊断。例如，对 <code>account.primaryOwner()</code> 的调用会产生类似以下的错误。</p><figure class="highlight subunit"><table><tr><td class="code"><pre><span class="line"><span class="keyword">error: </span>cannot call function returning non-Sendable type &#x27;Person?&#x27; across actors</span><br></pre></td></tr></table></figure><!--Note that the `primaryOwner()` function as defined above can still be used with actor-isolated code. For example, we can define a function to get the name of the primary owner, like this:--><p>请注意，上面定义的 <code>primaryOwner()</code> 函数仍然可以用于被 actor 隔离的代码。例如，我们可以定义一个函数来获取 primary owner 的名字，像这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">primaryOwnerName</span>() -&gt; <span class="type">String</span>? &#123;</span><br><span class="line">    <span class="keyword">return</span> primaryOwner()<span class="operator">?</span>.name</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The `primaryOwnerName()` function is safe to asynchronously call across actors because `String` (and therefore `String?`) conforms to `Sendable`.--><p><code>primaryOwnerName()</code> 函数可以安全地跨 actor 异步调用，因为 <code>String</code>（因此也包括<code>String?</code>）符合<code>Sendable</code>。</p><h3 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h3><!--The restrictions on cross-actor references only work so long as we can ensure that the code that might execute concurrently with actor-isolated code is considered to be non-isolated. For example, consider a function that schedules report generation at the end of the month:--><p>对跨 actor 引用的限制，只有在我们能确保，可能与被 actor 隔离的代码同时执行的代码，被视为不被隔离的情况下才有效。例如，一个计划生成月末报告的函数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">endOfMonth</span>(<span class="params">month</span>: <span class="type">Int</span>, <span class="params">year</span>: <span class="type">Int</span>) &#123;</span><br><span class="line">    <span class="comment">// Schedule a task to prepare an end-of-month report.</span></span><br><span class="line">    detach &#123;</span><br><span class="line">      <span class="keyword">let</span> transactions <span class="operator">=</span> <span class="keyword">await</span> <span class="keyword">self</span>.transactions(month: month, year: year)</span><br><span class="line">      <span class="keyword">let</span> report <span class="operator">=</span> <span class="type">Report</span>(accountNumber: <span class="keyword">self</span>.accountNumber, transactions: transactions)</span><br><span class="line">      <span class="keyword">await</span> report.email(to: <span class="keyword">self</span>.accountOwnerEmailAddress)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--A task created with `detach` runs concurrently with all other code. If the closure passed to `detach` were to be actor-isolated, we would introduce a data race on access to the mutable state on `BankAccount`. Actors prevent this data race by specifying that a `@Sendable` closure (described in [`Sendable` and `@Sendable` closures][se302], and used in the definition of `detach` in the [Structured Concurrency][sc] proposal) is always non-isolated. Therefore, it is required to asynchronously access any actor-isolated declarations.--><p>用 <code>detach</code> 创建的任务会与所有其他代码同时运行。如果传递给 <code>detach</code> 的闭包是被 actor 隔离的，我们将在访问 <code>BankAccount</code> 的 mutable 状态时引入 data race。actor 通过指定 <code>@Sendable</code> 闭包总是不被隔离的，来防止这种数据竞赛（在 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md"><code>Sendable</code> 和 <code>@Sendable</code> 闭包</a>中描述，并在<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发</a>提案中用于 <code>detach</code> 的定义）。因此，它需要异步访问任何被 actor 隔离的声明。</p><!--A closure that is not `@Sendable` cannot escape the concurrency domain in which it was formed. Therefore, such a closure will be actor-isolated if it is formed within an actor-isolated context. This is useful, for example, when applying sequence algorithms like `forEach` where the provided closure will be called serially:--><p>一个不是 <code>@Sendable</code> 的闭包不能脱离它所形成的并发域。因此，如果这样的闭包是在一个被 actor 隔离的上下文中形成的，那么它将是被 actor 隔离的。这一点很有用，例如，当应用在 <code>forEach</code> 这样的 Sequence 算法时，所提供的闭包将被连续调用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">close</span>(<span class="params">distributingTo</span> <span class="params">accounts</span>: [<span class="type">BankAccount</span>]) <span class="keyword">async</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> transferAmount <span class="operator">=</span> balance <span class="operator">/</span> accounts.count</span><br><span class="line"></span><br><span class="line">    accounts.forEach &#123; account <span class="keyword">in</span>    <span class="comment">// okay, closure is actor-isolated to `self`</span></span><br><span class="line">      balance <span class="operator">=</span> balance <span class="operator">-</span> transferAmount            </span><br><span class="line">      <span class="keyword">await</span> account.deposit(amount: transferAmount)</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">await</span> thief.deposit(amount: balance)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--A closure formed within an actor-isolated context is actor-isolated if it is non-`@Sendable`, and non-isolated if it is `@Sendable`. For the examples above:--><p>在一个被 actor 隔离的上下文中形成的闭包，如果它是非 <code>@Sendable</code>，则是被 actor 隔离的，如果它是 <code>@Sendable</code>，则是不被隔离的。对于上面的例子：</p><!--* The closure passed to `detach` is non-isolated because that function requires a `@Sendable` function to be passed to it.* The closure passed to `forEach` is actor-isolated to `self` because it takes a non-`@Sendable` function.--><ul><li>传递给 <code>detach</code> 的闭包是不被隔离的，因为该函数需要传递一个 <code>@Sendable</code> 闭包给它。</li><li>传递给 <code>forEach</code> 的闭包是与 <code>self</code> 隔离的，因为它需要一个非 <code>@Sendable</code> 函数。</li></ul><h3 id="Actor-重入"><a href="#Actor-重入" class="headerlink" title="Actor 重入"></a>Actor 重入</h3><!--Actor-isolated functions are [reentrant](https://en.wikipedia.org/wiki/Reentrancy_(computing)). When an actor-isolated function suspends, reentrancy allows other work to execute on the actor before the original actor-isolated function resumes, which we refer to as *interleaving*. Reentrancy eliminates a source of deadlocks, where two actors depend on each other, can improve overall performance by not unnecessarily blocking work on actors, and offers opportunities for better scheduling of (e.g.) higher-priority tasks. However, it means that actor-isolated state can change across an `await` when an interleaved task mutates that state, meaning that developers must be sure not to break invariants across an await. In general, this is the [reason for requiring `await`](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#suspension-points) on asynchronous calls, because various state (e.g., global state) can change when a call suspends.--><p>被 actor 隔离的函数是<a href="https://en.wikipedia.org/wiki/Reentrancy_(%E8%AE%A1%E7%AE%97)">可重入的</a>。当一个被 actor 隔离的函数暂停时，可重入性允许其他工作在原本被 actor 隔离的函数恢复之前，在该 actor 上执行，我们称之为 <em>interleaving</em>（交织执行）。重入消除了一个死锁的来源，即两个 actor 相互依赖，可以通过减少 actor 非必要的阻塞，来提高整体性能，并为更好地调度（例如）优先级更高的任务提供机会。然而，这意味着当交织的任务修改状态时，被 actor 隔离的状态可能会跨 <code>await</code> 发生变化，这意味着开发者必须确保不打破跨 await 的不变性。一般来说，这是在异步调用中要求 <code>await</code> 的<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#suspension-points">原因</a>，因为当调用暂停时，各种状态（例如全局状态）会发生变化。</p><!--This section explores the issue of reentrancy with examples that illustrate both the benefits and problems with both reentrant and non-reentrant actors, and settles on re-entrant actors. Alternatives Considered provides potential future directions to provide more control of re-entrancy, including [non-reentrant actors](#non-reentrancy) and [task-chain reentrancy](#task-chain-reentrancy).--><p>本节会通过例子探讨重入问题，阐述重入 actor 和非重入 actor 的好处和问题，并最终确定使用重入 actor。Alternatives Considered 提供了潜在的未来方向，以提供对重入的更多控制，包括<a href="#non-reentrancy">非重入 actor</a> 和<a href="#task-chain-reentrancy">任务链重入</a>。</p><h4 id="可重入-actor-的交织执行"><a href="#可重入-actor-的交织执行" class="headerlink" title="可重入 actor 的交织执行"></a>可重入 actor 的交织执行</h4><!--Reentrancy means that execution of asynchronous actor-isolated functions may "interleave" at suspension points, leading to increased complexity in programming with such actors, as every suspension point must be carefully inspected if the code *after* it depends on some invariants that could have changed before it suspended.--><p>重入意味着异步被 actor 隔离的函数的执行可能会在暂停点上交织，导致用这种 actor 编程的复杂度增加，因为如果暂停点<strong>之后</strong>的代码依赖于一些可能在暂停前发生变化的不变量，则必须仔细检查每个暂停点。</p><!--Interleaving executions still respect the actor's "single-threaded illusion", i.e., no two functions will ever execute *concurrently* on any given actor. However they may *interleave* at suspension points. In broad terms this means that reentrant actors are *thread-safe* but are not automatically protecting from the "high level" kinds of races that may still occur, potentially invalidating invariants upon which an executing asynchronous function may be relying on. To further clarify the implications of this, let us consider the following actor, which thinks of an idea and then returns it, after telling its friend about it.--><p>交织执行仍然尊重 actor 的”单线程幻觉”，也就是说，没有两个函数会在任何给定的 actor 上<em>并发</em>地执行。然而，它们可以在暂停点上<em>交织</em>。从广义上讲，这意味着可重入 actor 是<em>线程安全的</em>，但无法自动保护状态，因为”上层”的数据竞赛仍然可能发生，有可能使执行中的异步函数所依赖的不变性失效。为了进一步说明这个问题的含义，让我们看看下面这个 actor，它想到了一个想法，然后在告诉它的朋友之后将其返回：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> friend: <span class="type">Friend</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// actor-isolated opinion</span></span><br><span class="line">  <span class="keyword">var</span> opinion: <span class="type">Judgment</span> <span class="operator">=</span> .noIdea</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">thinkOfGoodIdea</span>() <span class="keyword">async</span> -&gt; <span class="type">Decision</span> &#123;</span><br><span class="line">    opinion <span class="operator">=</span> .goodIdea                       <span class="comment">// &lt;1&gt;</span></span><br><span class="line">    <span class="keyword">await</span> friend.tell(opinion, heldBy: <span class="keyword">self</span>)  <span class="comment">// &lt;2&gt;</span></span><br><span class="line">    <span class="keyword">return</span> opinion <span class="comment">// 🤨                      // &lt;3&gt;</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">thinkOfBadIdea</span>() <span class="keyword">async</span> -&gt; <span class="type">Decision</span> &#123;</span><br><span class="line">    opinion <span class="operator">=</span> .badIdea                       <span class="comment">// &lt;4&gt;</span></span><br><span class="line">    <span class="keyword">await</span> friend.tell(opinion, heldBy: <span class="keyword">self</span>) <span class="comment">// &lt;5&gt;</span></span><br><span class="line">    <span class="keyword">return</span> opinion <span class="comment">// 🤨                     // &lt;6&gt;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--In the example above the `Person` can think of a good or bad idea, shares that opinion with a friend, and returns that opinion that it stored. Since the actor is reentrant this code is wrong and will return an arbitrary opinion if the actor begins to think of a few ideas at the same time.--><p>在上面的例子中，<code>Person</code> 可以想到一个好的或坏的想法，与一个朋友分享这个意见，并返回它所存储的意见。由于 actor 是可重入的，所以这段代码是错误的，如果 actor 同时开始思考几个想法，就会返回一个任意的意见：</p><!--This is exemplified by the following piece of code, exercising the `decisionMaker` actor:--><p>以下是典型的 <code>decisionMaker</code> actor 调用代码，可以说明这一点：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> goodThink <span class="operator">=</span> detach &#123; <span class="keyword">await</span> person.thinkOfGoodIdea() &#125;  <span class="comment">// runs async</span></span><br><span class="line"><span class="keyword">let</span> badThink <span class="operator">=</span> detach &#123; <span class="keyword">await</span> person.thinkOfBadIdea() &#125; <span class="comment">// runs async</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> shouldBeGood <span class="operator">=</span> <span class="keyword">await</span> goodThink.get()</span><br><span class="line"><span class="keyword">let</span> shouldBeBad <span class="operator">=</span> <span class="keyword">await</span> badThink.get()</span><br><span class="line"></span><br><span class="line"><span class="keyword">await</span> shouldBeGood <span class="comment">// could be .goodIdea or .badIdea ☠️</span></span><br><span class="line"><span class="keyword">await</span> shouldBeBad</span><br></pre></td></tr></table></figure><!--This snippet _may_ result (depending on timing of the resumptions) in the following execution:--><p>这个片段 <em>可能</em> 会产生（取决于恢复的时间）以下的执行顺序：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">opinion <span class="operator">=</span> .goodIdea                <span class="comment">// &lt;1&gt;</span></span><br><span class="line"><span class="comment">// 暂停: await friend.tell(...) // &lt;2&gt;</span></span><br><span class="line">opinion <span class="operator">=</span> .badIdea                 <span class="comment">// | &lt;4&gt; (!)</span></span><br><span class="line"><span class="comment">// 暂停: await friend.tell(...) // | &lt;5&gt;</span></span><br><span class="line"><span class="comment">// 恢复: await friend.tell(...)  // &lt;2&gt;</span></span><br><span class="line"><span class="keyword">return</span> opinion                     <span class="comment">// &lt;3&gt;</span></span><br><span class="line"><span class="comment">// 恢复: await friend.tell(...)  // &lt;5&gt;</span></span><br><span class="line"><span class="keyword">return</span> opinion                     <span class="comment">// &lt;6&gt;</span></span><br></pre></td></tr></table></figure><!--But it _may_ also result in the "naively expected" execution, i.e. without interleaving, meaning that the issue will only show up intermittently, like many race conditions in concurrent code.--><p>但它也 <em>可能</em> 导致 “naively expected” 的执行，即没有交织，这意味着问题只会间歇性地出现，就像并发代码中的许多竞赛条件。</p><!--The potential for interleaved execution at suspension points is the primary reason for the requirement that every suspension point be [marked by `await`](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#suspension-points) in the source code, even though `await` itself has no semantic effect. It is an indicator that any shared state might change across the `await`, so one should avoid breaking invariants across an `await`, or otherwise depending on the state "before" to be identical to the state "after".--><p>在暂停点上有可能出现交织执行，这是要求每个暂停点在源代码中<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#suspension-points">用 <code>await</code> 标记</a>的主要原因，尽管 <code>await</code> 本身没有语义作用。这是一个提示，表明任何共享状态都可能在 <code>await</code> 中发生变化，所以应该避免在 <code>await</code> 中破坏不变性，或者依赖 “之前”的状态与”之后”的状态一致。</p><!--Generally speaking, the easiest way to avoid breaking invariants across an `await` is to encapsulate state updates in synchronous actor functions. Effectively, synchronous code in an actor provides a [critical section](https://en.wikipedia.org/wiki/Critical_section), whereas an `await` interrupts a critical section. For our example above, we could effect this change by separating "opinion formation" from "telling a friend your opinion". Indeed, telling your friend your opinion might reasonably cause you to change your opinion!--><p>一般来说，避免跨 <code>await</code> 破坏不变性的最简单方法是将状态更新封装在同步 actor 函数中。实际上，actor 中的同步代码提供了一个<a href="https://en.wikipedia.org/wiki/Critical_section">临界区</a>，而 <code>await</code> 中断了一个临界区。对于我们上面的例子，我们可以通过将 “optinion fomration” 与 “telling a friend your opinion” 分开来实现这个修改。事实上，告诉你的朋友你的观点可能会合理地导致你改变你的观点！</p><h4 id="不可重入的-actor-的死锁"><a href="#不可重入的-actor-的死锁" class="headerlink" title="不可重入的 actor 的死锁"></a>不可重入的 actor 的死锁</h4><!--The opposite of reentrant actor functions are "non-reentrant" functions and actors. This means that while an actor is processing an incoming actor function call (message), it will *not* process any other message from its mailbox until it has completed running this initial function. Essentially, the entire actor is blocked from executing until that task completes.--><p>与可重入的 actor 函数相反的是”不可重入”的函数和 actor。这意味着当一个actor 正在处理一个传入的 actor 函数调用（消息）时，它将<em>不会</em>处理来自其邮箱的任何其他消息，直到它完成运行这个初始函数。本质上，整个 actor 都会被阻塞，直到该任务完成。</p><!--If we take the example from the previous section and use a non-reentrant actor, it will execute correctly, because no work can be scheduled on the actor until `friend.tell` has completed:--><p>如果我们以上一节的例子为例，使用一个非重入 actor，它将正确执行，因为在 <code>friend.tell</code> 完成之前，不能给 actor 安排工作：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// assume non-reentrant</span></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">DecisionMaker</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> friend: <span class="type">DecisionMaker</span></span><br><span class="line">  <span class="keyword">var</span> opinion: <span class="type">Judgment</span> <span class="operator">=</span> .noIdea</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">thinkOfGoodIdea</span>() <span class="keyword">async</span> -&gt; <span class="type">Decision</span> &#123;</span><br><span class="line">    opinion <span class="operator">=</span> .goodIdea                                   </span><br><span class="line">    <span class="keyword">await</span> friend.tell(opinion, heldBy: <span class="keyword">self</span>)</span><br><span class="line">    <span class="keyword">return</span> opinion <span class="comment">// ✅ always .goodIdea</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">thinkOfBadIdea</span>() <span class="keyword">async</span> -&gt; <span class="type">Decision</span> &#123;</span><br><span class="line">    opinion <span class="operator">=</span> .badIdea</span><br><span class="line">    <span class="keyword">await</span> friend.tell(opinion, heldBy: <span class="keyword">self</span>)</span><br><span class="line">    <span class="keyword">return</span> opinion <span class="comment">// ✅ always .badIdea</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--However, non-entrancy can result in deadlock if a task involves calling back into the actor. For example, let's stretch this example further and have our friend try to convince us to change a bad idea:--><p>然而，如果一个任务涉及到回调 actor，非重入就会导致死锁。例如，让我们进一步延伸这个例子，让我们的朋友试图说服我们改变一个坏主意：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">DecisionMaker</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">tell</span>(<span class="keyword">_</span> <span class="params">opinion</span>: <span class="type">Judgment</span>, <span class="params">heldBy</span> <span class="params">friend</span>: <span class="type">DecisionMaker</span>) <span class="keyword">async</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> opinion <span class="operator">==</span> .badIdea &#123;</span><br><span class="line">      <span class="keyword">await</span> friend.convinceOtherwise(opinion)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--With non-reentrant actors, `thinkOfGoodIdea()` will succeed under this implementation, because `tell` essentially does nothing. However, `thinkOfBadIdea()` will deadlock because the original decision maker (call it `A`) is locked when it calls `tell` on another decision maker (call it `B`). `B` then tries to convince `A` otherwise, but that call cannot execute because `A` is already locked. Hence, the actor itself deadlocks and cannot progress.--><p>对于非重入的 actor，<code>thinkOfGoodIdea()</code> 在这种实现下会成功，因为 <code>tell</code> 基本上什么都不做。然而，<code>thinkOfBadIdea()</code> 将陷入僵局，因为原来的决策者（称为 A）在对另一个决策者（称为 B）调用 <code>tell</code> 时被锁定。然后，<code>B</code> 试图说服 <code>A</code>，但该调用无法执行，因为 <code>A</code> 已经被锁定。因此，actor 自身进入死锁，无法进行下去。</p><!--> The term "deadlock" used in these discussions refer to actors asynchronously waiting on "each other," or on "future work of self". No thread blocking is necessary to manifest this issue.--><blockquote><p>这些讨论中使用的术语”死锁”是指 actor 异步地等待”彼此”或”自己的未来工作”。对于这个问题来说，线程阻塞不是必要条件。</p></blockquote><!--In theory, a fully non-reentrant model would also deadlock when calling asynchronous functions on `self`. However, since such calls are statically determinable to be on `self`, they would execute immediately and therefore not block.--><p>理论上，一个完全不重入的模型在调用 <code>self</code> 上的异步函数时也会出现死锁。然而，由于这种调用可以静态地确定是在 <code>self</code> 上，它们会立即执行，因此不会阻塞。</p><!--Deadlocks with non-reentrant actors could be detected with runtime tools that detect cyclic call graphs once they've occurred, much like tools exist to find reference cycles in data structures at runtime. However, such deadlocks cannot generally be identified statically (e.g., with the compiler or static analysis), because call graphs require whole-program knowledge and can change dynamically depending on the data provided to the program.--><p>可以用运行时工具来检测非重入 actor 的死锁，这些工具可以在死锁发生后检测循环调用图，就像在运行时检测数据结构中的引用循环一样。然而，这种死锁通常不能被静态地识别（例如，用编译器或静态分析），因为调用图需要整个程序的知识，并且会根据提供给程序的数据动态变化。</p><!--Deadlocked actors would be sitting around as inactive zombies forever. Some runtimes solve deadlocks like this by making every single actor call have a timeout (such timeouts are already useful for distributed actor systems). This would mean that each `await` could potentially `throw`, and that either timeouts or deadlock detection would have to always be enabled. We feel this would be prohibitively expensive, because we envision actors being used in the vast majority of concurrent Swift applications. It would also muddy the waters with respect to cancellation, which is intentionally designed to be explicit and cooperative. Therefore, we feel that the approach of automatically cancelling on deadlocks does not fit well with the direction of Swift Concurrency.--><p>死锁的 actor 将永远作为不活跃的僵尸呆在那里。一些运行时为了解决这样的死锁问题，让每个 actor 的调用都有一个超时时间（分布式 actor 系统已经证实这种超时机制确实行之有效）。这意味着每个 <code>await</code> 都有可能 <code>throw</code>，而超时或死锁检测都必须始终被启用。我们认为这个代价太过昂贵，因为我们设想在绝大多数并发的 Swift 应用中都会使用 actor。这也会搅浑取消机制，取消机制是有意设计成明确和可合作的。因此，我们觉得自动取消死锁的方法并不符合 Swift 并发的方向。</p><h4 id="不可重入的-actor-非必要的阻塞"><a href="#不可重入的-actor-非必要的阻塞" class="headerlink" title="不可重入的 actor 非必要的阻塞"></a>不可重入的 actor 非必要的阻塞</h4><!--Consider an actor that handles the download of various images and maintains a cache of what it has downloaded to make subsequent accesses faster:--><p>想想看一个处理各种图片下载的 actor，并保存对已下载内容的缓存，以使后续访问更快：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// assume non-reentrant</span></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">ImageDownloader</span> &#123; </span><br><span class="line">  <span class="keyword">var</span> cache: [<span class="type">URL</span>: <span class="type">Image</span>] <span class="operator">=</span> [:]</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">getImage</span>(<span class="keyword">_</span> <span class="params">url</span>: <span class="type">URL</span>) <span class="keyword">async</span> -&gt; <span class="type">Image</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">let</span> cachedImage <span class="operator">=</span> cache[url] &#123;</span><br><span class="line">      <span class="keyword">return</span> cachedImage</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">let</span> data <span class="operator">=</span> <span class="keyword">await</span> download(url)</span><br><span class="line">    <span class="keyword">let</span> image <span class="operator">=</span> <span class="keyword">await</span> <span class="type">Image</span>(decoding: data)</span><br><span class="line">    <span class="keyword">return</span> cache[url, <span class="keyword">default</span>: image]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This actor is functionally correct, whether it is re-entrant or not. However, if it is non-reentrant, it will completely serialize the download of images: once a single client asked for an image, all other clients are blocked from starting any requests--even ones that would hit the cache or which ask for images at different URLs---until that first client has had its image fully downloaded and decoded.--><p>这个 actor 在功能上是正确的，不管它是否是可重入的。然而，如果它是非重入的，它将逐个执行图片的下载：一旦一个客户端请求了一张图片，所有其他的客户端将被阻止开始任何请求 – 即使是那些会击中缓存或在不同的 URL 上请求图片的请求 – 直到第一个客户端将其图片完全下载和解码。</p><!--With a reentrant actor, multiple clients can fetch images independently, so that (say) they can all be at different stages of downloading and decoding an image. The serialized execution of partial tasks on the actor ensures that the cache itself can never get corrupted. At worst, two clients might ask for the same image URL at the same time, in which there will be some redundant work. --><p>而使用可重入的 actor 时，多个客户端可以独立地获取图像，这样（比如）他们都可以处于下载和解码图像的不同阶段。actor 上部分任务的串行执行确保了缓存本身不会被破坏。在最坏的情况下，两个客户可能会同时要求获得相同的图像URL，其中会有一些多余的工作。</p><h4 id="已有的实践"><a href="#已有的实践" class="headerlink" title="已有的实践"></a>已有的实践</h4><!--There are a number of existing actor implementations that have considered the notion of reentrancy:--><p>有一些现有的 actor 实现已经考虑了重入的概念：</p><!--* Erlang/Elixir ([gen_server](https://medium.com/@eduardbme/erlang-gen-server-never-call-your-public-interface-functions-internally-c17c8f28a1ee)) showcases a simple "loop/deadlock" scenario and how to detect and fix it,* Akka ([Persistence persist/persistAsync](https://doc.akka.io/docs/akka/current/persistence.html#relaxed-local-consistency-requirements-and-high-throughput-use-cases) is effectively _non-reentrant behavior by default_, and specific APIs are designed to allow programmers to _opt into_ reentrant whenever it would be needed. In the linked documentation `persistAsync` is the re-entrant version of the API, and it is used _very rarely_ in practice. Akka persistence and this API has been used to implement bank transactions and process managers, by relying on the non-reentrancy of `persist()` as a killer feature, making implementations simple to understand and _safe_. Note that Akka is built on top of Scala, which does not provide `async`/`await`. This means that mailbox-processing methods are more synchronous in nature, and rather than block the actor while waiting for a response, they would handle the response as a separate message receipt.* Orleans ([grains](https://dotnet.github.io/orleans/docs/grains/reentrancy.html)) are also non-reentrant by default, but offer extensive configuration around reentrancy. Grains and specific methods can be marked as being re-entrant, and there is even a dynamic mechanism by which one can implement a run-time predicate to determine whether an invocation can interleave. Orleans is perhaps closest to the Swift approach described here, because it is built on top of a language that provides `async`/`await` (C#). Note that Orleans *had* a feature called [call-chain reentrancy](https://dotnet.github.io/orleans/docs/grains/reentrancy.html#reentrancy-within-a-call-chain), which we feel is a promising potential direction: we cover it later in this proposal in our section on [task-chain reentrancy](#task-chain-reentrancy).--><ul><li>Erlang&#x2F;Elixir（<a href="https://medium.com/@eduardbme/erlang-gen-server-never-call-your-public-interface-function-internally-c17c8f28a1ee">gen_server</a>）展示了一个简单的 “loop&#x2F;deadlock” 场景以及如何检测和修复它。</li><li>Akka（<a href="https://doc.akka.io/docs/akka/current/persistence.html#relaxed-local-consistency-requirements-and-high-throughput-use-cases">Persistence persist&#x2F;persistAsync</a>)实际上是默认 <em>非重入的</em> ，特定的 API 被设计为允许程序员在需要时 <em>选择</em> 重入。在链接的文档中，<code>persistAsync</code> 是 API 的可重入版本，它在实践中很少被使用。Akka 持久化和这个 API 已经被用来实现银行交易和流程管理，依靠 <code>persist()</code> 的非重入性作为杀手锏，使得实现简单易懂且 <em>安全</em> 。注意，Akka 是建立在 Scala 之上的，Scala 不提供 <code>async</code>&#x2F;<code>await</code>。这意味着邮箱处理方法在本质上更具有同步性，与其在等待响应时阻塞 actor，不如将响应作为一个单独的消息接收来处理。</li><li>Orleans（<a href="https://dotnet.github.io/orleans/docs/grains/reentrancy.html">grains</a>）默认也是非重入的，但围绕重入性提供了可拓展的配置。Grains 和特定的方法可以被标记为可重入，甚至还有一种动态机制，可以实现一个运行时的断言来确定一个调用是否可以交织执行。Orleans 也许最接近这里描述的 Swift 方法，因为它是建立在提供 <code>async</code>&#x2F;<code>await</code> 的语言之上的（C#）。请注意，Orleans <em>有</em>一个叫做<a href="https://dotnet.github.io/orleans/docs/grains/reentrancy.html#reentrancy-within-a-call-chain">调用链重入</a>的功能，我们认为这是一个很有前途的潜在方向：我们在本提案后面的<a href="#task-chain-reentrancy">任务链重入</a>一节中会介绍它。</li></ul><h4 id="Reentrancy-总结"><a href="#Reentrancy-总结" class="headerlink" title="Reentrancy 总结"></a>Reentrancy 总结</h4><!--This proposal provides only reentrant actors. However, the [Future Directions](#future-directions) section describes potential future design directions that could add opt-in non-reentrancy.--><p>本提案只提供了可重入的 actor。然而，<a href="#future-directions">未来方向</a>小节里阐述了潜在的未来设计方向，可以增加可选的非重入性。</p><!--> **Rationale**: Reentrancy by default all but eliminates the potential for deadlocks. Moreover, it helps ensure that actors can make timely progress within a concurrent system, and that a particular actor does not end up unnecessarily blocked on a long-running asynchronous operation (say, downloading a file). The mechanisms for ensuring safe interleaving, such as using synchronous code when performing mutations and being careful not to break invariants across `await` calls, are already present in the proposal.--><blockquote><p><strong>理由</strong>。默认情况下，重入可以消除死锁的可能性。此外，它还有助于确保 actor 在并发系统中能及时取得进展，并确保特定的 actor 不会在长期运行的异步操作（例如，下载文件）中出现不必要的阻塞。确保安全交织的机制，例如在执行突变时使用同步代码，并注意不破坏跨 <code>await</code> 调用的不变性，已经存在于该提案中。</p></blockquote><h3 id="Protocol-conformances"><a href="#Protocol-conformances" class="headerlink" title="Protocol conformances"></a>Protocol conformances</h3><!--All actor types implicitly conform to a new protocol, `Actor`:--><p>所有的 actor 类型都默认遵循一个新的协议，<code>Actor</code>。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Actor</span> : <span class="title class_ inherited__">AnyObject</span>, <span class="title class_ inherited__">Sendable</span> &#123; &#125;</span><br></pre></td></tr></table></figure><!--> **Note**: The definition of the `Actor` protocol is intentionally left blank. The [custom executors proposal][customexecs] will introduce requirements into the `Actor` protocol. These requirements will be implicitly synthesized by the implementation when not explicitly provided, but can be explicitly provided to allow actors to control their own serialized execution.--><blockquote><p><strong>注意</strong>。<code>Actor</code> 协议的定义是故意留白的。<a href="https://github.com/rjmccall/swift-evolution/blob/custom-executors/proposals/0000-custom-executors.md">Custom executor 提案</a>将在 <code>Actor</code> 协议中引入实现要求。当没有明确提供时，这些要求将被实现隐含地合成，但可以明确地提供，以允许 actor 控制自己的串行执行。</p></blockquote><!--The `Actor` protocol can be used to write generic operations that work across all actors, including extending all actor types with new operations. As with actor types, instance properties, functions, and subscripts defined on the `Actor` protocol (including extensions thereof) are actor-isolated to the `self` actor. For example, --><p><code>Actor</code> 协议可以用来编写适用于所有 actor 的通用操作，包括用新的操作扩展所有 actor 类型。与 actor 类型一样，在 <code>Actor</code> 协议上定义的实例属性、函数和下标（包括其扩展）是与 <code>self</code> actor 隔离的。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">DataProcessible</span>: <span class="title class_ inherited__">Actor</span> &#123;  <span class="comment">// only actor types can conform to this protocol</span></span><br><span class="line">  <span class="keyword">var</span> data: <span class="type">Data</span> &#123; <span class="keyword">get</span> &#125;           <span class="comment">// actor-isolated to self</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">DataProcessible</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">compressData</span>() -&gt; <span class="type">Data</span> &#123;    <span class="comment">// actor-isolated to self</span></span><br><span class="line">    <span class="comment">// use data synchronously</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">MyProcessor</span> : <span class="title class_ inherited__">DataProcessible</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> data: <span class="type">Data</span>                   <span class="comment">// okay, actor-isolated to self</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">doSomething</span>() &#123;</span><br><span class="line">    <span class="keyword">let</span> newData <span class="operator">=</span> compressData()   <span class="comment">// okay, calling actor-isolated method on self</span></span><br><span class="line">    <span class="comment">// use new data</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">doProcessing</span>&lt;<span class="type">T</span>: <span class="type">DataProcessible</span>&gt;(<span class="params">processor</span>: <span class="type">T</span>) <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="keyword">await</span> processor.compressData() <span class="comment">// not actor-isolated, so we must interact asynchronously with the actor</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--No other kind of concrete type (class, enum, struct, etc.) can conform to the `Actor` protocol, because they cannot define actor-isolated operations.--><p>其他类型的具体类型（类、枚举、结构等）都不能遵循 <code>Actor</code> 协议，因为它们不能定义被 actor 隔离的操作。</p><!--Actors can also conform to protocols with `async` requirements, because all clients will already have to interact with those requirements asynchronously, giving the actor the ability to protect its isolated state. For example:--><p>Actor 也可以遵循具有 <code>async</code> 要求的协议，因为所有的客户都已经不得不与这些实现要求进行异步交互，使得 actor 有能力保护它隔离的状态。比如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Server</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">send</span>&lt;<span class="type">Message</span>: <span class="type">MessageType</span>&gt;(<span class="params">message</span>: <span class="type">Message</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Message</span>.<span class="type">Reply</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">MyActor</span>: <span class="title class_ inherited__">Server</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">send</span>&lt;<span class="type">Message</span>: <span class="type">MessageType</span>&gt;(<span class="params">message</span>: <span class="type">Message</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Message</span>.<span class="type">Reply</span> &#123; <span class="comment">// okay: this method is actor-isolated to &#x27;self&#x27;, satisfies asynchronous requirement</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Actors cannot otherwise be made to conform to non-`Actor` protocols with synchronous requirements. However, there is a separate proposal on [controlling actor isolation][isolationcontrol] that allows such conformances when they can implemented in a manner that does not reference any mutable actor state.--><p>Actor 不能被用来遵循具有同步要求的非 <code>Actor</code> 协议。然而，有一个关于<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md">控制 actor 隔离</a>的单独提议，当它们能够以不引用任何 mutable actor 状态的方式实现时，允许这种遵循。</p><h2 id="具体设计"><a href="#具体设计" class="headerlink" title="具体设计"></a>具体设计</h2><h3 id="Actors-1"><a href="#Actors-1" class="headerlink" title="Actors"></a>Actors</h3><!--An actor type can be declared with the `actor` keyword:--><p>Actor 类型可以用 <code>actor</code> 关键字来声明：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">/// Declares a new type BankAccount</span></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Each instance of the actor represents a unique actor. The term "actor" can be used to refer to either an instance or the type; where necessary, one can refer to the "actor instance" or "actor type" to disambiguate.--><p>每个 actor 的实例都代表一个唯一的 actor。术语 “actor” 可以用来指代实例或类型；在必要时，人们可以使用 ”actor 实例” 或 “actor 类型 “来消除歧义。</p><!--Actors are similar to other concrete nominal types in Swift (enums, structs, and classes). Actor types can have `static` and instance methods, properties, and subscripts. They have stored properties and initializers like structs and classes. They are reference types like classes, but do not support inheritance, and therefore do not have (or need) features such as `required` and `convenience` initializers, overriding, or `class` members, `open` and `final`. Where actor types differ in behavior from other types is primarily driven by the rules of actor isolation, described below.--><p>Actor 类似于 Swift 中的其他具体名义类型（enum, struct 和 class）。Actor 类型可以有 <code>static</code> 和实例方法、属性和下标。它们像 stuct 和 class 一样有存储属性和初始化器。它们像 class 一样是引用类型，但不支持继承，因此没有（或不需要）诸如 <code>required</code> 和 <code>convenience</code> 初始化器、override 、或 <code>class</code> 成员、<code>open</code> 和 <code>final</code> 等功能。Actor 类型在行为上与其他类型的不同之处主要是由 actor isolation 的规则驱动的，如下所述。</p><!--By default, the instance methods, properties, and subscripts of an actor have an isolated `self` parameter. This is true even for methods added retroactively on an actor via an extension, like any other Swift type. Static methods, properties, and subscripts do not have a `self` parameter that is an instance of the actor, so they are not actor-isolated.--><p>默认情况下，actor 的实例方法、属性和下标有一个隔离的 <code>self</code> 参数。即使是通过扩展在 actor 上追加的方法也是如此，就像其他 Swift 类型一样。静态方法、属性和下标没有一个作为 actor 实例的 <code>self</code> 参数，所以它们不是与被 actor 隔离的。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">acceptTransfer</span>(<span class="params">amount</span>: <span class="type">Double</span>) <span class="keyword">async</span> &#123; <span class="comment">// actor-isolated</span></span><br><span class="line">    balance <span class="operator">+=</span> amount</span><br><span class="line">  &#125;</span><br><span class="line">&#125;  </span><br></pre></td></tr></table></figure><h3 id="Actor-隔离检查"><a href="#Actor-隔离检查" class="headerlink" title="Actor 隔离检查"></a>Actor 隔离检查</h3><!--Any given declaration in a program is either actor-isolated or is non-isolated. A function (including accessors) is actor-isolated if it is defined on an actor type (including protocols where `Self` conforms to `Actor`, and extensions thereof). A mutable instance property or instance subscript is actor-isolated if it is defined on an actor type. Declarations that are not actor-isolated are called non-isolated.--><p>程序中的任何给定的声明要么是被 actor 隔离的，要么是不被隔离的。如果一个函数（包括访问器）定义在一个 actor 类型上（包括 <code>Self</code> 符合 <code>Actor</code> 的协议，以及其扩展），那么它就是被 actor 隔离的。如果一个 mutable 的实例属性或实例下标是定义在一个 actor 类型上的，那么它就是被 actor 隔离的。不被 actor 隔离的声明被称为不被隔离的（non-isolated）。</p><!--The actor isolation rules are checked in a number of places, where two different declarations need to be compared to determine if their usage together maintains actor isolation. There are several such places:--><p>Actor isolation 规则会在很多地方被检查，在这些地方，需要对两个不同的声明进行比较，以确定它们在一起的使用是否保持了 actor 的隔离。有几个这样的地方：</p><!--* When the definition of one declaration (e.g., the body of a function) references another declaration, e.g., calling a function, accessing a property, or evaluating a subscript.* When one declaration satisfies a protocol requirement.--><ul><li>当一个声明的定义（例如，一个函数的主体）引用另一个声明时，例如，调用一个函数，访问一个属性，或执行一个下标。</li><li>当一个声明满足了一个协议要求时。</li></ul><!--We'll describe each scenario in detail.--><p>我们将详细地描述每种情况。</p><h4 id="引用和-actor-的隔离"><a href="#引用和-actor-的隔离" class="headerlink" title="引用和 actor 的隔离"></a>引用和 actor 的隔离</h4><!--An actor-isolated non-`async` declaration can only be synchronously accessed from another declaration that is isolated to the same actor. For synchronous access to an actor-isolated function, the function must be called from another actor-isolated function. For synchronous access to an actor-isolated instance property or instance subscript, the instance itself must be actor-isolated.--><p>一个被 actor 隔离的非 <code>async</code> 声明只能从另一个与同一 actor 隔离的声明中被同步访问。对于同步访问一个被 actor 隔离的函数，该函数必须从另一个被 actor 隔离的函数中调用。对于同步访问一个被 actor 隔离的实例属性或实例下标，该实例本身必须是被 actor 隔离的。</p><!--An actor-isolated declaration can be asynchronously accessed from any declaration, whether it is isolated to another actor or is non-isolated. Such accesses are asynchronous operations, and therefore must be annotated with `await`. Semantically, the progam will switch to the actor to perform the synchronous operation, and then switch back to the caller's executor afterward.--><p>一个被 actor 隔离的声明可以从任何声明中被异步访问，无论它是被隔离到另一个 actor 还是不被隔离。这样的访问是异步操作，因此必须用<code> await</code> 来注释。从语义上讲，程序将切换 actor 来执行同步操作，之后再切换回调用者的 executor。</p><!--For example:--><p>例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">MyActor</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> name: <span class="type">String</span></span><br><span class="line">  <span class="keyword">var</span> counter: <span class="type">Int</span> <span class="operator">=</span> <span class="number">0</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">f</span>()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">MyActor</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">g</span>(<span class="params">other</span>: <span class="type">MyActor</span>) <span class="keyword">async</span> &#123;</span><br><span class="line">    <span class="built_in">print</span>(name)          <span class="comment">// okay, name is non-isolated</span></span><br><span class="line">     <span class="built_in">print</span>(other.name)    <span class="comment">// okay, name is non-isolated</span></span><br><span class="line">     <span class="built_in">print</span>(counter)       <span class="comment">// okay, g() is isolated to MyActor</span></span><br><span class="line">     <span class="built_in">print</span>(other.counter) <span class="comment">// error: g() is isolated to &quot;self&quot;, not &quot;other&quot;</span></span><br><span class="line">     f()                  <span class="comment">// okay, g() is isolated to MyActor</span></span><br><span class="line">     <span class="keyword">await</span> other.f()      <span class="comment">// okay, other is not isolated to &quot;self&quot; but asynchronous access is permitted</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Protocol-conformance"><a href="#Protocol-conformance" class="headerlink" title="Protocol conformance"></a>Protocol conformance</h4><!--When a given declaration (the "witness") satisfies a protocol requirement (the "requirement"), the protocol requirement can be satisfied by the witness if:--><p>当某项声明（”witness”）满足某项协议要求（”requirement”）时，在以下情况下，witness 可以满足该协议要求：</p><!--* The requirement is `async`, or* the requirement and witness are both actor-isolated.--><ul><li>Requirement 是 <code>async</code>，或</li><li>Requirement 和 witness 都是被 actor 隔离的。</li></ul><!--An actor can satisfy an asynchronous requirement because any uses of the requirement are asynchronous, and can therefore suspend until the actor is available to execute them. Note that an actor can satisfy an asynchronous requirement with a synchronous one, in which case the normal notion of asynchronously accessing a synchronous declaration on an actor applies. For example:--><p>一个 actor 可以满足异步的要求，因为对该要求的任何使用都是异步的，因此可以暂停，直到该 actor 可以执行它们。请注意，一个 actor 可以用一个同步的实现来满足一个异步的实现需求，在这种情况下，异步访问一个 actor 上的同步声明的正常规则是适用的。比如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Server</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">send</span>&lt;<span class="type">Message</span>: <span class="type">MessageType</span>&gt;(<span class="params">message</span>: <span class="type">Message</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Message</span>.<span class="type">Reply</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">MyServer</span> : <span class="title class_ inherited__">Server</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">send</span>&lt;<span class="type">Message</span>: <span class="type">MessageType</span>&gt;(<span class="params">message</span>: <span class="type">Message</span>) <span class="keyword">throws</span> -&gt; <span class="type">Message</span>.<span class="type">Reply</span> &#123; <span class="operator">...</span> &#125;  <span class="comment">// okay, asynchronously accessed from clients of the protocol</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Partial-applications"><a href="#Partial-applications" class="headerlink" title="Partial applications"></a>Partial applications</h3><!--Partial applications of isolated functions are only permitted when the expression is a direct argument whose corresponding parameter is non-escaping and non-Sendable. For example:--><p>只有当表达式是一个直接的参数，其对应的参数是 non-escaping 和 non-Sendable 时，才允许隔离函数的 partial applications。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">runLater</span>&lt;<span class="type">T</span>&gt;(<span class="keyword">_</span> <span class="params">operation</span>: <span class="keyword">@escaping</span> () -&gt; <span class="type">T</span>) -&gt; <span class="type">T</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">A</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">f</span>(<span class="keyword">_</span>: <span class="type">Int</span>) -&gt; <span class="type">Double</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">g</span>() -&gt; <span class="type">Double</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">useAF</span>(<span class="params">array</span>: [<span class="type">Int</span>]) &#123;</span><br><span class="line">    array.map(<span class="keyword">self</span>.f)                     <span class="comment">// okay</span></span><br><span class="line">    detach(operation: <span class="keyword">self</span>.g)             <span class="comment">// error: self.g has non-sendable type () -&gt; Double that cannot be converted to a @Sendable function type</span></span><br><span class="line">    runLater(<span class="keyword">self</span>.g)                      <span class="comment">// error: self.g has escaping function type () -&gt; Double</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--These restrictions follow from the actor isolation rules for the "desugaring" of partial applications to closures. The two erroneous cases above fall out from the fact that the closure would be non-isolated in a closure that performs the call, so access to the actor-isolated function `g` would have to be asynchronous. Here are the "desugared" forms of the partial applications:--><p>这些限制来自于 actor 对闭包的 partial applications “desugaring” 后生效的隔离规则。上面两个错误的例子是由于在执行调用的闭包中，闭包将是不被隔离的，所以对被 actor 隔离的函数 <code>g</code> 的访问必须是异步的。下面是 partial application 的 “desugared” 形式：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">A</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">useAFDesugared</span>(<span class="params">a</span>: <span class="type">A</span>, <span class="params">array</span>: [<span class="type">Int</span>]) &#123;</span><br><span class="line">    array.map &#123; f(<span class="variable">$0</span>) &#125; )      <span class="comment">// okay</span></span><br><span class="line">    detach &#123; g() &#125;             <span class="comment">// error: self is non-isolated, so call to `g` cannot be synchronous</span></span><br><span class="line">    runLater &#123; g() &#125;           <span class="comment">// error: self is non-isolated, so the call to `g` cannot be synchronous</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Key-paths"><a href="#Key-paths" class="headerlink" title="Key paths"></a>Key paths</h3><!--A key path cannot involve a reference to an actor-isolated declaration:--><p>一个 keypath 不能涉及对被 actor 隔离声明的引用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">A</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> storage: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> kp <span class="operator">=</span> \<span class="type">A</span>.storage  <span class="comment">// error: key path would permit access to actor-isolated storage</span></span><br></pre></td></tr></table></figure><!--> **Rationale**: Allowing the formation of a key path that references an actor-isolated property or subscript would permit accesses to the actor's protected state from outside of the actor isolation domain. As an alternative to this rule, we could remove the `Sendable` conformance from key paths, such that one could form key paths to actor-isolated state but they could not be shared.--> <blockquote><p><strong>理由</strong>。允许生成引用 actor isolation 的属性或下标的 keypath，将允许从 actor isolation 域之外访问 actor 的受保护状态。作为这一规则的替代方案，我们可以从 keypath 中移除 <code>Sendable</code> 的遵循，这样人们就可以形成指向被 actor 隔离状态的 keypath，但它们不能被共享。</p></blockquote><h3 id="inout-参数"><a href="#inout-参数" class="headerlink" title="inout 参数"></a>inout 参数</h3><!--Actor-isolated stored properties can be passed into synchronous functions via `inout` parameters, but it is ill-formed to pass them to asynchronous functions via `inout` parameters. For example:--><p>被 actor 隔离的存储属性可以通过 <code>inout</code> 参数传递到同步函数中，但通过 <code>inout</code> 参数传递到异步函数中是不符合规定的。比如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">modifiesSynchronously</span>(<span class="keyword">_</span>: <span class="keyword">inout</span> <span class="type">Double</span>) &#123; &#125;</span><br><span class="line"><span class="keyword">func</span> <span class="title function_">modifiesAsynchronously</span>(<span class="keyword">_</span>: <span class="keyword">inout</span> <span class="type">Double</span>) <span class="keyword">async</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">wildcardBalance</span>() <span class="keyword">async</span> &#123;</span><br><span class="line">    modifiesSynchronously(<span class="operator">&amp;</span>balance)        <span class="comment">// okay</span></span><br><span class="line">    <span class="keyword">await</span> modifiesAsynchronously(<span class="operator">&amp;</span>balance) <span class="comment">// error: actor-isolated property &#x27;balance&#x27; cannot be passed &#x27;inout&#x27; to an asynchronous function</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;  </span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">C</span> &#123; <span class="keyword">var</span> state : <span class="type">Double</span> &#125;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Pair</span> &#123; <span class="keyword">var</span> a, b : <span class="type">Double</span> &#125;</span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">A</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> someC : <span class="type">C</span></span><br><span class="line">  <span class="keyword">var</span> somePair : <span class="type">Pair</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">inoutModifications</span>() <span class="keyword">async</span> &#123;</span><br><span class="line">    modifiesSynchronously(<span class="operator">&amp;</span>someC.state)        <span class="comment">// okay</span></span><br><span class="line">    <span class="keyword">await</span> modifiesAsynchronously(<span class="operator">&amp;</span>someC.state) <span class="comment">// not okay</span></span><br><span class="line">    modifiesSynchronously(<span class="operator">&amp;</span>somePair.a)         <span class="comment">// okay</span></span><br><span class="line">    <span class="keyword">await</span> modifiesAsynchronously(<span class="operator">&amp;</span>somePair.a)  <span class="comment">// not okay</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--> **Rationale**: this restriction prevents exclusivity violations where the modification of the actor-isolated `balance` is initiated by passing it as `inout` to a call that is then suspended, and another task executed on the same actor then attempts to access `balance`. Such an access would then result in an exclusivity violation that will terminate the program. While the `inout` restriction is not required for memory safety (because errors will be detected at runtime), the default re-entrancy of actors makes it very easy to introduce non-deterministic exclusivity violations. Therefore, we introduce this restriction to eliminate that class of problems that where a race would trigger an exclusivity violation.--><blockquote><p><strong>理由</strong>：这个限制可以防止违反独占性，在这种情况下，对被 actor 隔离的 <code>balance</code> 的修改是通过把它作为 <code>inout</code> 传递给一个调用而开始的，这个调用被暂停，然后在同一个 actor 上执行的另一个任务试图访问 <code>balance</code>。这样的访问将导致违反独占性，从而终止程序。虽然 <code>inout</code> 的限制对于内存安全来说是不需要的（因为错误会在运行时被检测到），但 actor 的默认重入性使得它很容易引入非确定性的独占性违规。因此，我们引入了这个限制，以消除这类问题，即竞赛会触发独占性的违规。</p></blockquote><h3 id="Actor-与-Objective-C-的交互"><a href="#Actor-与-Objective-C-的交互" class="headerlink" title="Actor 与 Objective-C 的交互"></a>Actor 与 Objective-C 的交互</h3><!--An actor type can be declared `@objc`, which implicitly provides conformance to `NSObjectProtocol`:--><p>一个 actor 类型可以被声明为 <code>@objc</code>，它隐式地提供了对 <code>NSObjectProtocol</code> 的遵循：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span> <span class="keyword">actor</span> <span class="title class_">MyActor</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--A member of an actor can only be `@objc` if it is either `async` or is not isolated to the actor. Synchronous code that is within the actor's isolation domain can only be invoked on `self` (in Swift). Objective-C does not have knowledge of actor isolation, so these members are not permitted to be exposed to Objective-C. For example:--><p>Actor 的成员只有在 <code>async</code> 或不被 actor 隔离的情况下才能标注为 <code>@objc</code>。在 actor 的隔离域内的同步代码只能在 <code>self</code> 上调用（在 Swift 中）。Objective-C 并不了解 actor 的隔离，所以这些成员不允许暴露在 Objective-C 中。比如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span> <span class="keyword">actor</span> <span class="title class_">MyActor</span> &#123;</span><br><span class="line">    <span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">synchronous</span>() &#123; &#125; <span class="comment">// error: part of actor&#x27;s isolation domain</span></span><br><span class="line">    <span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">asynchronous</span>() <span class="keyword">async</span> &#123; &#125; <span class="comment">// okay: asynchronous, exposed to Objective-C as a method that accepts a completion handler</span></span><br><span class="line">    <span class="keyword">@objc</span> <span class="keyword">nonisolated</span> <span class="keyword">func</span> <span class="title function_">notIsolated</span>() &#123; &#125; <span class="comment">// okay: non-isolated</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a>代码兼容性</h2><!--This proposal is mostly additive, and should not break source compatibility. The `actor` contextual keyword to introduce actors is a parser change that does not break existing code, and the other changes are carefully staged so they do not change existing code. Only new code that introduces actors or actor-isolation attributes will be affected.--><p>这个建议主要是补充性的，不应该会破坏源代码的兼容性。引入 Actor 的 <code>actor</code> 上下文关键字是对解析器的改变，不会破坏现有的代码，其他的改变也是精心设计的，所以它们不会改变现有的代码。只有引入 actors 或 actor-isolation 属性的新代码会受到影响。</p><h2 id="对于-ABI-稳定性的影响"><a href="#对于-ABI-稳定性的影响" class="headerlink" title="对于 ABI 稳定性的影响"></a>对于 ABI 稳定性的影响</h2><!--This is purely additive to the ABI. Actor isolation itself is a static notion that is not part of the ABI.--><p>这纯粹是对 ABI 的补充。Actor isolation 本身是一个静态概念，不是 ABI 的一部分。</p><h2 id="对于-API-稳定性的影响"><a href="#对于-API-稳定性的影响" class="headerlink" title="对于 API 稳定性的影响"></a>对于 API 稳定性的影响</h2><!--Nearly all changes in actor isolation are breaking changes, because the actor isolation rules require consistency between a declaration and its users:--><p>几乎所有 actor isolation 的改变都是破坏性的改变，因为 actor isolation 规则要求声明和其用户之间的一致性。</p><!--* A class cannot be turned into an actor or vice versa.* The actor isolation of a public declaration cannot be changed.--><ul><li>一个 class 不能变成一个 actor，反之亦然。</li><li>一个公共声明的 actor isolation 不能被改变。</li></ul><h2 id="未来方向"><a href="#未来方向" class="headerlink" title="未来方向"></a>未来方向</h2><h3 id="Non-reentrancy"><a href="#Non-reentrancy" class="headerlink" title="Non-reentrancy"></a>Non-reentrancy</h3><!--We could introduce a `@reentrant` attribute may be added to any actor-isolated function, actor, or extension of an actor to describe how it is reentrant. The attribute would have several forms:--><p>我们可以引入一个 <code>@reentrant</code> 注解，它可以被添加到任何被 actor 隔离的函数、actor 或 actor 的扩展中，以描述它是如何重入的。该属性将有几种形式：</p><!--* `@reentrant`: Indicates that each potential suspension point within the function bodies covered by the attribute is reentrant.* `@reentrant(never)`: Indicates that each potential suspension point within the function bodies covered by the attribute is non-reentrant.--><ul><li><code>@reentrant</code>: 表示被注解的函数体中的每个潜在暂停点是可重入的。</li><li><code>@reentrant(never)</code>: 表示被注解的函数体中的每个潜在暂停点都是不可重入的。</li></ul><!--A non-reentrant potential suspension point prevents any other asynchronous call from executing on the actor until it has completed. Note that asynchronous calls to non-reentrant async functions directly on `self` are exempted from this check, so an actor can asynchronously call itself without producing a deadlock.--><p>一个不可重入的潜在暂停点会阻止任何其他异步调用在 actor 上执行，直到它完成。请注意，直接在 <code>self</code> 上对非重进异步函数的异步调用不受这个检查的影响，所以一个 actor 可以异步调用自己而不产生死锁。</p><!--> **Rationale**: Allowing direct calls on `self` eliminates an obvious set of deadlocks, and requires only the same static knowledge as actor-isolation checking for synchronous access to actor-isolated state.--><blockquote><p><strong>理由</strong>。允许直接调用 <code>self</code> 可以消除一系列明显的死锁，并且只需要与被 actor 隔离检查相同的静态信息，就可以同步访问被 actor 隔离的状态。</p></blockquote><!--It is an error to have a `@reentrant` attribute on a non-isolated function, non-actor type, or extension of a non-actor type. Only one `@reentrant` attribute may occur on a given declaration. The reentrancy of an actor-isolated non-type declaration is determined by finding a suitable `@reentrant` attribute. The search is as follows:--><p>在一个不被隔离的函数、非 actor 类型或非 actor 类型的扩展上使用 <code>@reentrant</code> 注解是错误的。只有给定的声明中可以使用 <code>@reentrant</code> 注解。一个被 actor 隔离的非类型声明的可重入性是通过寻找一个合适的 <code>@reentrant</code> 属性来确定的。搜索的路径如下：</p><!--1. The declaration itself.2. If the declaration is a non-type member of an extension, the extension.3. If the declaration is a non-type member of a type (or extension thereof), the type definition.--><ol><li>声明本身。</li><li>如果声明是一个扩展的非类型成员，则是该扩展。</li><li>如果声明是一个类型（或其扩展）的非类型成员，则是类型定义。</li></ol><!--If there is no suitable `@reentrant` attribute, an actor-isolated declaration is reentrant.--><p>如果没有合适的 <code>@reentrant</code> 注解，被 actor 隔离的声明就是可重入的。</p><!--Here's an example illustrating how the `@reentrant` attribute can be applied at various points:--><p>下面有一个例子，说明如何在不同地方使用 <code>@reentrant</code> 注解：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">Stage</span> &#123;</span><br><span class="line">  <span class="meta">@reentrant</span>(never) <span class="keyword">func</span> <span class="title function_">f</span>() <span class="keyword">async</span> &#123; <span class="operator">...</span> &#125;    <span class="comment">// not reentrant</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">g</span>() <span class="keyword">async</span> &#123; <span class="operator">...</span> &#125;                      <span class="comment">// reentrant</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@reentrant</span>(never)</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Stage</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">h</span>() <span class="keyword">async</span> &#123; <span class="operator">...</span> &#125;                      <span class="comment">// not reentrant</span></span><br><span class="line">  <span class="meta">@reentrant</span> <span class="keyword">func</span> <span class="title function_">i</span>() <span class="keyword">async</span> &#123; <span class="operator">...</span> &#125;           <span class="comment">// reentrant</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">actor</span> <span class="title class_">InnerChild</span> &#123;                          <span class="comment">// reentrant, not affected by enclosing extension</span></span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">j</span>() <span class="keyword">async</span> &#123; <span class="operator">...</span> &#125;                    <span class="comment">// reentrant</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">nonisolated</span> <span class="keyword">func</span> <span class="title function_">k</span>() <span class="keyword">async</span> &#123; <span class="operator">..</span> &#125;     <span class="comment">// okay, reentrancy is uninteresting</span></span><br><span class="line">  <span class="keyword">nonisolated</span> <span class="meta">@reentrant</span> <span class="keyword">func</span> <span class="title function_">l</span>() <span class="keyword">async</span> &#123; <span class="operator">..</span> &#125; <span class="comment">// error: @reentrant on non-actor-isolated</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@reentrant</span> <span class="keyword">func</span> <span class="title function_">m</span>() <span class="keyword">async</span> &#123; <span class="operator">...</span> &#125; <span class="comment">// error: @reentrant on non-actor-isolated</span></span><br></pre></td></tr></table></figure><!--The attribute approach is not the only possible design here. At an implementation level, the actual blocking will be handled at each asynchronous call site. Instead of an attribute that affects potentially many asynchronous calls, we could introduce a different form of `await` that does the blocking, e.g.,--><p>注解不是这里唯一可能的设计方向。在实现层面上，实际的阻塞将在每个异步调用点处理。我们可以引入一个不同形式的 <code>await</code> 来进行阻塞，而不是一个可能影响许多异步调用的注解，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">await</span>(blocking) friend.tell(opinion, heldBy: <span class="keyword">self</span>)</span><br></pre></td></tr></table></figure><h3 id="Task-chain-reentrancy"><a href="#Task-chain-reentrancy" class="headerlink" title="Task-chain reentrancy"></a>Task-chain reentrancy</h3><!--The discussion of reentrant and non-reentrant actors treats reentrancy as a binary choice, where all forms of reentrancy are considered to be equally likely to introduce hard-to-reason-about data races. However, a frequent and usually quite understandable way of interacting between actors which are simply "conversations" between two or more actors in order fo fulfill some initial request. In synchronous code, it's common to have two or more different classes call back into each other with synchronous calls. For example, here is a silly implementation of `isEven` that uses mutual recursion between two classes:--><p>关于可重入和不可重入 actor 的讨论都将重入视为一种二元选择，所有形式的重入都被认为是同样可能引入难以解释的数据竞赛。然而，常见的、通常很容易理解的 actor 之间的交互方式，它只是两个或多个 actor 之间的”对话”，以实现一些原始的请求。在同步代码中，让两个或多个不同的 class 以同步调用的方式相互回调是很常见的。例如，这里是 <code>isEven</code> 的一个愚蠢的实现，它在两个 class 之间使用了相互递归：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">OddOddySync</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> evan: <span class="type">EvenEvanSync</span>!</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">isOdd</span>(<span class="keyword">_</span> <span class="params">n</span>: <span class="type">Int</span>) -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> n <span class="operator">==</span> <span class="number">0</span> &#123; <span class="keyword">return</span> <span class="literal">true</span> &#125;</span><br><span class="line">    <span class="keyword">return</span> evan.isEven(n <span class="operator">-</span> <span class="number">1</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">EvenEvanSync</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> oddy: <span class="type">OddOddySync</span>!</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">isEven</span>(<span class="keyword">_</span> <span class="params">n</span>: <span class="type">Int</span>) -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> n <span class="operator">==</span> <span class="number">0</span> &#123; <span class="keyword">return</span> <span class="literal">false</span> &#125;</span><br><span class="line">    <span class="keyword">return</span> oddy.isOdd(n <span class="operator">-</span> <span class="number">1</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This code is depending on the two methods of these classes to effectively be "reentrant" within the same call stack, because one will call into the other (and vice-versa) as part of the computation. Now, take this example and make it asynchronous using actors:--><p>这段代码依赖于这些 class 的两个方法在同一个调用堆栈中有效的”可重入”，因为其中一个会调用到另一个（反之亦然）作为计算的一部分。现在，以这个例子为例，用 <code>actor</code> 使其成为异步的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@reentrant</span>(never)</span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">OddOddy</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> evan: <span class="type">EvenEvan</span>!</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">isOdd</span>(<span class="keyword">_</span> <span class="params">n</span>: <span class="type">Int</span>) <span class="keyword">async</span> -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> n <span class="operator">==</span> <span class="number">0</span> &#123; <span class="keyword">return</span> <span class="literal">true</span> &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> evan.isEven(n <span class="operator">-</span> <span class="number">1</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@reentrant</span>(never)</span><br><span class="line"><span class="keyword">actor</span> <span class="title class_">EvenEvan</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> oddy: <span class="type">OddOddy</span>!</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">isEven</span>(<span class="keyword">_</span> <span class="params">n</span>: <span class="type">Int</span>) <span class="keyword">async</span> -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> n <span class="operator">==</span> <span class="number">0</span> &#123; <span class="keyword">return</span> <span class="literal">false</span> &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> oddy.isOdd(n <span class="operator">-</span> <span class="number">1</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Under `@reentrant(never)`, this code will deadlock, because a call from `EvanEvan.isEven` to `OddOddy.isOdd` will then depend on another call to `EvanEvan.isEven`, which cannot proceed until the original call completes. One would need to make these methods to be reentrant to eliminate the deadlock.--><p>在 <code>@reentrant(never)</code> 下，这段代码会出现死锁，因为从 <code>EvanEvan.isEven</code> 到 <code>OddOddy.isOdd</code> 的调用将依赖于对 <code>EvanEvan.isEven</code> 的另一次调用，在原始调用完成之前，无法继续。我们需要使这些方法成为可重入的，以消除死锁。</p><!--With Swift embracing [Structured Concurrency][sc] as a core building block of its concurrency story, we may be able to do better than outright banning reentrancy. In Swift, every asynchronous operation is part of a `Task` which encapsulates the general computation taking place, and every asynchronous operation spawned from such task becomes a child task of the current task. Therefore, it is possible to know whether a given asynchronous call is part of the same task hierarchy, which is the rough equivalent to being in the same call stack in synchronous code.--><p>随着 Swift 将<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发</a>作为其并发功能的核心组件，我们可能会比直接禁止重入做得更好。在 Swift 中，每个异步操作都是 <code>Task</code> 的一部分，<code>Task</code> 封装了正在进行的一般计算，从这种任务中产生的每个异步操作都成为当前任务的一个子任务。因此，可以知道某个异步调用是否属于同一个任务层次，这大致相当于在同步代码中处于同一个调用栈。</p><!--We could introduce a new kind of reentrancy, *task-chain reentrancy*, which allows reentrant calls on behalf of the given task or any of its children. This resolves both the deadlock we encountered in the `convinceOtherwise` example from the section on [deadlocks](#deadlocks-with-non-reentrant-actors) as well as the mutually-recursive `isEven` example above, while still preventing reentrancy from unrelated tasks. This reentrancy therefore mimics synchronous code more closely, eliminating many deadlocks without allowing unrelated interleavings to break the high-level invariants of an actor.--><p>我们可以引入一种新的重入，<em>任务链重入</em>，它允许代表给定任务或其任何子任务的重入调用。这既解决了我们在 <a href="#deadlocks-with-non-reentrant-actors">deadlocks</a> 一节中的 <code>convinceOtherwise</code> 例子中遇到的死锁，也解决了上面 <code>isEven</code> 例子中的相互递归，同时还能防止不相关任务的再入。因此，这种重入更接近于模仿同步代码，消除了许多死锁，而不需要允许无关的交织执行破坏 actor 的高层不变性。</p><!--There are a few reasons why we are not currently comfortale including task-chain reentrancy in the proposal:--><p>我们目前没有把任务链重入性纳入到提案中有这几个原因：</p><!--* The task-based reentrancy approach doesn't seem to have been tried at scale. Orleans documents support for [reentrancy in a call chain](https://dotnet.github.io/orleans/docs/grains/reentrancy.html#reentrancy-within-a-call-chain), but the implementation was fairly limited and it was eventually [removed](https://twitter.com/reubenbond/status/1349725703634251779). From the Orleans experience, it is hard to assess whether the problem is with the idea or the specific implementation.* We do not yet know of an efficient implementation technique for this approach within the actor runtime.--><ul><li>基于任务的重入方法似乎还没有被大规模地尝试过。Orleans 记录了对<a href="https://dotnet.github.io/orleans/docs/grains/reentrancy.html#reentrancy-within-a-call-chain">调用链中的重入的支持</a>，但实施相当有限，最终被<a href="https://twitter.com/reubenbond/status/1349725703634251779">删除</a>。从 Orleans 的经验来看，很难评估问题是出在想法上还是具体的实现上。</li><li>我们还不知道这种方法在 actor 运行时中的有效实现方式。</li></ul><!--If we can address the above, task-chain reentrancy can be introduced into the actor model with another spelling of the reentrancy attribute such as `@reentrant(task)`, and may provide the best default.--><p>如果我们能够解决上述问题，任务链重入可以通过重入属性的另一种参数，如 <code>@reentrant(task)</code>，引入到 actor 模型中，并可能提供最佳的默认值。</p><h2 id="其它替代方案"><a href="#其它替代方案" class="headerlink" title="其它替代方案"></a>其它替代方案</h2><h3 id="Actor-继承"><a href="#Actor-继承" class="headerlink" title="Actor 继承"></a>Actor 继承</h3><!--Earlier pitches and the first reviewed version of this proposal allowed actor inheritance. Actor inheritance followed the rules of class inheritance, albeit with specific additional rules required to maintain actor isolation:--><p>早期的草案和本提案的第一个版本允许 actor 继承。Actor 的继承遵循 class 的继承规则，尽管有特定的额外规则来维持 actor 的隔离：</p><!--* An actor could not inherit from a class, and vice-versa.* An overriding declaration must not be more isolated than the overridden declaration.--><ul><li>一个 actor 不能继承于一个 class ，反之亦然。</li><li>一个 override 的声明不能比被 override 的声明的隔离等级高。</li></ul><!--Subsequent review discussion determined that the conceptual cost of actor inheritance outweighed its usefulness, so it has been removed from this proposal. The form that actor inheritance would take in the language is well-understand from prior iterations of this proposal and its implementation, so this feature could be re-introduced at a later time.--><p>随后的审查讨论确定，actor 继承的概念成本超过了它的实用性，所以它已经从这个提案中删除。actor 继承在语言中采取的形式，在本提案的先前迭代和实施中已被充分理解，所以这一特性可以在以后重新引入。</p><h3 id="跨-actor-lets"><a href="#跨-actor-lets" class="headerlink" title="跨 actor lets"></a>跨 actor lets</h3><!--This proposal allows synchronous access to `let` properties on an actor instance from anywhere within the same module as the actor is defined:--><p>这个提案允许从定义 actor 的同一 module 的任何地方同步访问 actor 实例上的 <code>let</code> 属性：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// in module BankActors</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">actor</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">let</span> accountNumber: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">print</span>(<span class="params">account</span>: <span class="type">BankAccount</span>) &#123;</span><br><span class="line">  <span class="built_in">print</span>(account.accountNumber) <span class="comment">// okay: synchronous access to an actor&#x27;s let property</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Outside of the module, access must be asynchronous:--><p>在 module 之外的访问必须是异步的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> BankActors</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">otherPrint</span>(<span class="params">account</span>: <span class="type">BankAccount</span>) <span class="keyword">async</span> &#123;</span><br><span class="line">  <span class="built_in">print</span>(account.accountNumber)         <span class="comment">// error: cannot synchronously access immutable &#x27;let&#x27; outside the actor&#x27;s module</span></span><br><span class="line">  <span class="built_in">print</span>(<span class="keyword">await</span> account.accountNumber)   <span class="comment">// okay to asynchronously access</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The requirement for asynchronous access from outside of the module preserves a longstanding freedom for library implementors, which allows a public `let` to be refactored into a `var` without breaking any clients. It is consistent with Swift's policy of maximizing the freedom for library implementors to alter the implementation without breaking clients. Without requiring asynchronous access from other modules, the `otherPrint(account:)` function above were permitted to reference `accountNumber` synchronously. If the author of `BankActors` then changed the account number into a `var`, it would break existing client code:--><p>对 module 之外必须异步访问的要求，为库实现者提供了长期的自由度，使得 public 的 <code>let</code> 可以被重构为 <code>var</code> 的同时，不会破坏任何客户端。这与 Swift 的原则一致，即在不破坏客户端的情况下，最大限度地增加库实现者改变实现的自由。如果不要求其他 module 必须异步访问，上述的 <code>otherPrint(account:)</code> 函数将会被允许同步引用 <code>accountNumber</code>，<code>BankActors</code> 的作者随后将账号改为 <code>var</code>，就会破坏现有的客户端代码。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">actor</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">var</span> accountNumber: <span class="type">Int</span>     <span class="comment">// version 2 makes this mutable, but would break clients if synchronous access to &#x27;let&#x27;s were allowed outside the module</span></span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><!--There are a number of other language features that take this same approach of reducing boilerplate and simplifying the language within a module, then requiring the use of additional language features when an entity is used from outside the module. For example:--><p>还有一些其他的语言功能也采取了同样的方法，即在一个 module 内减少模板和简化语言，然后当一个实体从 module 外使用时要求使用额外的语言功能。比如说：</p><!--* Access control defaults to `internal`, so you can use a declaration across your whole module but have to explicitly opt in to making it available outside your module (e.g., via `public`). In other words, you can ignore access control until the point where you need to make something `public` for use from another module.* The implicit memberwise initializer of a struct is `internal`. You need to write a `public` initializer yourself to commit to allowing that struct to be initialized with exactly that set of parameters.* Inheritance from a class is permitted by default when the superclass is in the same module. To inherit from a superclass defined in a different module, that superclass must be explicitly marked `open`. You can ignore `open` until you want to guarantee this ability to clients outside of the module.* Overriding a declaration in a class is permitted by default when the overridden declaration is in the same module. To override from a declaration in a different module, that overrides declaration must be explicitly marked `open`.--><ul><li>访问控制默认为 <code>internal</code>，所以你可以在你的整个 module 中使用一个声明，但必须明确选择在你的 module 之外使用它（例如，通过 <code>public</code>）。换句话说，你可以忽略访问控制，直到你需要把某个东西 <code>public</code> 给其他 module 使用。</li><li>struct 默认生成的构造器是 <code>internal</code> 的。你需要自己写一个 <code>public</code> 的构造器，以承诺允许该 struct 被初始化为一组确切的参数。</li><li>当父类在同一个 module 中时，默认允许从 class 中继承。要继承一个定义在不同 module 中的父类，该父类必须被明确标记为 <code>open</code>。你可以忽略 <code>open</code>，直到你想向 module 外的用户保证这种能力。</li><li>当被 override 的声明在同一个 module 中时，默认允许覆盖一个 class 中的声明。要从不同 module 的声明中覆盖，该覆盖的声明必须明确标记为 <code>open</code>。</li></ul><!--SE-0313 "[Improved control over actor isolation][isolationcontrol]" provides an explicit way to give clients the freedom to synchronously access immutable actor state via the `nonisolated` keyword, e.g.,--><p>SE-0313 “<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md">改进对 actor 隔离的控制</a>“提供了一种显式的方式，让客户通过 <code>nonisolated</code> 关键字同步访问不可变的 actor 状态的自由，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// in module BankActors</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">actor</span> <span class="title class_">BankAccount</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">nonisolated</span> <span class="keyword">let</span> accountNumber: <span class="type">Int</span>  <span class="comment">// can be accessed synchronously from any module due to the explicit &#x27;nonisolated&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The original accepted version of this proposal required *all* access to immutable actor storage be asynchronous, and left any synchronous access to explicit `nonisolated` annotations as spelled out in [SE-0313][isolationcontrol]. However, experience with that model showed that it had a number of problems that affected teachability of the model:--><p>这个提案最初通过的版本要求<em>所有</em>对不可变 actor 存储属性的访问都是异步的，并将任何同步的访问留给 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md">SE-0313</a> 中所阐述的显式 <code>nonisolated</code> 注解。然而，尝试使用该模型的过程表明，它有很多问题，影响了该模型的上手难度：</p><!--* Developers were almost immediately confronted with the need to use `nonisolated` when writing actor code. This goes against the principle of [progressive disclosure](https://www.interaction-design.org/literature/topics/progressive-disclosure) that Swift tries to follow for advanced features. Aside from `nonisolated let`, uses of `nonisolated` are fairly rare.* Immutable state is a key tool for writing safe concurrency code. A `let` of `Sendable` type is conceptually safe to reference from concurrency code, and works in other contexts (e.g., local variables). Making some immutable state concurrency-safe while other state is not complicates the story about data-race-safe concurrent programming. Here's an example of the existing restrictions around `@Sendable`, which were defined in [SE-0302][se302]:--><ul><li>开发人员在编写 actor 代码时，几乎会立即需要使用 <code>nonisolated</code>。这违背了 Swift 试图让高级功能遵循的<a href="https://www.interaction-design.org/literature/topics/progressive-disclosure">渐进式披露</a>原则。除了 <code>nonisolated let</code> 之外，<code>nonisolated</code> 的使用相当少。</li><li>不可变的状态是编写安全并发代码的一个关键工具。<code>Sendable</code> 类型的 <code>let</code> 在概念上是安全的，可以从并发代码中引用，并在其他情况下工作（例如，局部变量）。使一些不可变的状态成为并发安全的，而其他状态则不是，这就使数据安全的并发编程的故事变得复杂。下面是一个围绕 <code>@Sendable</code> 的现有限制的例子，这些限制是在 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md">SE-0302</a> 中定义的：<figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">test</span>() &#123;</span><br><span class="line">  <span class="keyword">let</span> total <span class="operator">=</span> <span class="number">100</span></span><br><span class="line">  <span class="keyword">var</span> counter <span class="operator">=</span> <span class="number">0</span></span><br><span class="line"> </span><br><span class="line">  asyncDetached &#123;</span><br><span class="line">    <span class="built_in">print</span>(total) <span class="comment">// okay to reference immutable state</span></span><br><span class="line">    <span class="built_in">print</span>(counter) <span class="comment">// error, cannot reference a `var` from a @Sendable closure</span></span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  counter <span class="operator">+=</span> <span class="number">1</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><!--By allowing synchronous access to actor `let`s within a module, we provide a smoother learning curve for actor isolation and embrace (rather than subvert) the longstanding and pervasive idea that immutable data is safe for concurrency, while still addressing the concerns from the second review that unrestricted synchronous access to actor `let`s is implicitly committing a library author to never make that state mutable. It follows existing precedent in the Swift language of making in-module interactions simpler than interactions across modules.--><p>通过允许在 module 内同步访问 actor 的 <code>let</code> 属性，我们为 actor 隔离提供了一个更平滑的学习曲线，并接纳（而不是颠覆）长期以来普遍存在的想法，即不可变的数据对并发是安全的，同时仍然解决了第二次审查中提出的担忧，即对 actor <code>let</code> 的无限制的同步访问隐含了一个库作者对该状态永不变的承诺。它遵循 Swift 语言中现有的先例，使 module 内的交互比跨 module 的交互更简单。</p><h2 id="Revision-history"><a href="#Revision-history" class="headerlink" title="Revision history"></a>Revision history</h2><ul><li>Changes in the post-review amendment to the proposal:<ul><li>Cross-after references to instance <code>let</code> properties from a different module must be asynchronous; within the same module they will be synchronous.</li></ul></li><li>Changes in the final accepted version of the proposal:<ul><li>Cross-actor references to instance <code>let</code> properties must be asynchronous.</li></ul></li><li>Changes in the second reviewed proposal:<ul><li>Escaping closures can now be actor-isolated; only <code>@Sendable</code> prevents isolation.</li><li>Removed actor inheritance. It can be considered at some future point.</li><li>Added “cross-actor lets” to Alternatives Considered. While there is no change to the proposed direction, the issue is explained here for further discussion.</li><li>Replaced <code>Task.runDetached</code> with <code>detach</code> to match updates to the <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">Structured Concurrency proposal</a>.</li></ul></li><li>Changes in the seventh pitch:<ul><li>Removed isolated parameters and <code>nonisolated</code> from this proposal. They’ll come in a follow-up proposal on <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0313-actor-isolation-control.md">controlling actor isolation</a>.</li></ul></li><li>Changes in the sixth pitch:<ul><li>Make the instance requirements of <code>Actor</code> protocols actor-isolated to <code>self</code>, and allow actor types to conform to such protocols using actor-isolated witnesses.</li><li>Reflow the “Proposed Solution” section to get the bigger ideas out earlier.</li><li>Remove <code>nonisolated(unsafe)</code>.</li></ul></li><li>Changes in the fifth pitch:<ul><li>Drop the prohibition on having multiple <code>isolated</code> parameters. We don’t need to ban it.</li><li>Add the <code>Actor</code> protocol back, as an empty protocol whose details will be filled in with a subsequent proposal for <a href="https://github.com/rjmccall/swift-evolution/blob/custom-executors/proposals/0000-custom-executors.md">custom executors</a>.</li><li>Replace <code>ConcurrentValue</code> with <code>Sendable</code> and <code>@concurrent</code> with <code>@Sendable</code> to track the evolution of <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md">SE-0302</a>.</li><li>Clarify the presentation of actor isolation checking.</li><li>Add more examples for non-isolated declarations.</li><li>Added a section on isolated or “sync” actor types.</li></ul></li><li>Changes in the fourth pitch:<ul><li>Allow cross-actor references to actor properties, so long as they are reads (not writes or <code>inout</code> references)</li><li>Added <code>isolated</code> parameters, to generalize the previously-special behavior of <code>self</code> in an actor and make the semantics of <code>nonisolated</code> more clear.</li><li>Limit <code>nonisolated(unsafe)</code> to stored instance properties. The prior definition was far too broad.</li><li>Clarify that <code>super</code> is isolated if <code>self</code> is.</li><li>Prohibit references to actor-isolated declarations in key paths.</li><li>Clarify the behavior of partial applications.</li><li>Added a “future directions” section describing isolated protocol conformances.</li></ul></li><li>Changes in the third pitch:<ul><li>Narrow the proposal down to only support re-entrant actors. Capture several potential non-reentrant designs in the Alternatives Considered as possible future extensions.</li><li>Replaced <code>@actorIndependent</code> attribute with a <code>nonisolated</code> modifier, which follows the approach of <code>nonmutating</code> and ties in better with the “actor isolation” terminology (thank you to Xiaodi Wu for the suggestion).</li><li>Replaced “queue” terminology with the more traditional “mailbox” terminology, to try to help alleviate confusion with Dispatch queues.</li><li>Introduced “cross-actor reference” terminology and the requirement that cross-actor references always traffic in <code>Sendable</code> types.</li><li>Reference <code>@concurrent</code> function types from their separate proposal.</li><li>Moved Objective-C interoperability into its own section.</li><li>Clarify the “class-like” behaviors of actor types, such as satisfying an <code>AnyObject</code> conformance.</li></ul></li><li>Changes in the second pitch:<ul><li>Added a discussion of the tradeoffs with actor reentrancy, performance, and deadlocks, with various examples, and the addition of new attribute <code>@reentrant(never)</code> to disable reentrancy at the actor or function level.</li><li>Removed global actors; they will be part of a separate document.</li><li>Separated out the discussion of data races for reference types.</li><li>Allow asynchronous calls to synchronous actor methods from outside the actor.</li><li>Removed the <code>Actor</code> protocol; we’ll tackle customizing actors and executors in a separate proposal.</li><li>Clarify the role and behavior of actor-independence.</li><li>Add a section to “Alternatives Considered” that discusses actor inheritance.</li><li>Replace “actor class” with “actor”.</li></ul></li><li>Original pitch <a href="https://github.com/DougGregor/swift-evolution/blob/6fd3903ed348b44496b32a39b40f6b6a538c83ce/proposals/nnnn-actors.md">document</a></li></ul>]]>
    </content>
    <id>https://kemchenj.github.io/2021-04-25/</id>
    <link href="https://kemchenj.github.io/2021-04-25/"/>
    <published>2021-04-24T16:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md">SE-0306 Actors</a></p>
</blockquote>
<ul>
<li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md">SE-0306</a></li>
<li>Authors: <a href="https://github.com/rjmccall">John McCall</a>, <a href="https://github.com/DougGregor">Doug Gregor</a>, <a href="https://github.com/ktoso">Konrad Malawski</a>, <a href="https://github.com/lattner">Chris Lattner</a></li>
<li>Review Manager: <a href="https://github.com/jckarter">Joe Groff</a></li>
<li>Status: <strong>Implemented (Swift 5.5)</strong></li>
<li>Decision Notes: <a href="https://forums.swift.org/t/accepted-with-modification-se-0306-actors/47662">Acceptance</a>, <a href="https://forums.swift.org/t/se-0306-actors/45734">First Review</a>, <a href="https://forums.swift.org/t/se-0306-second-review-actors/47291">Second Review</a></li>
<li>Implementation: Partially available in <a href="https://swift.org/download/#snapshots">recent <code>main</code> snapshots</a> behind the flag <code>-Xfrontend -enable-experimental-concurrency</code></li>
</ul>
<!--
## Table of Contents

* [Introduction](#introduction)
* [Proposed solution](#proposed-solution)
   * [Actors](#actors-1)
   * [Actor isolation](#actor-isolation)
   * [Cross-actor references and Sendable types](#cross-actor-references-and-sendable-types)
   * [Closures](#closures)
   * [Actor reentrancy](#actor-reentrancy)
      * ["Interleaving" execution with reentrant actors](#interleaving-execution-with-reentrant-actors)
      * [Deadlocks with non-reentrant actors](#deadlocks-with-non-reentrant-actors)
      * [Unnecessary blocking with non-reentrant actors](#unnecessary-blocking-with-non-reentrant-actors)
      * [Existing practice](#existing-practice)
      * [Reentrancy Summary](#reentrancy-summary)
   * [Protocol conformances](#protocol-conformances)
* [Detailed design](#detailed-design)
   * [Actors](#actors-2)
   * [Actor isolation checking](#actor-isolation-checking)
      * [References and actor isolation](#references-and-actor-isolation)
      * [Protocol conformance](#protocol-conformance)
   * [Partial applications](#partial-applications)
   * [Key paths](#key-paths)
   * [inout parameters](#inout-parameters)
   * [Actor interoperability with Objective-C](#actor-interoperability-with-objective-c)
* [Source compatibility](#source-compatibility)
* [Effect on ABI stability](#effect-on-abi-stability)
* [Effect on API resilience](#effect-on-api-resilience)
* [Future Directions](#future-directions)
   * [Non-reentrancy](#non-reentrancy)
   * [Task-chain reentrancy](#task-chain-reentrancy)
* [Alternatives considered](#alternatives-considered) 
   * [Actor inheritance](#actor-inheritance)
   * [Cross-actor lets](#cross-actor-lets)
* [Revision history](#revision-history)
-->

<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--
The Swift concurrency model intends to provide a safe programming model that statically detects [data races](https://en.wikipedia.org/wiki/Race_condition#Data_race) and other common concurrency bugs. The [Structured Concurrency][sc] proposal introduces a way to define concurrent tasks and provides data-race safety for functions and closures. This model is suitable for a number of common design patterns, including things like parallel maps and concurrent callback patterns, but is limited to working with state that is captured by closures.
-->

<p>Swift 并发模型计划提供一个安全的编程模型，静态地检测 <a href="https://en.wikipedia.org/wiki/Race_condition#Data_race">data-races</a> 和其他常见的并发错误。<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发</a>提案引入了一种定义并发任务的方法，并为函数和闭包提供了安全的 data-race。这个模型适用于许多常见的设计模式，包括像 parallel maps 和并发回调模式，但只限于处理由闭包捕获的状态。</p>
<!--
Swift includes classes, which provide a mechanism for declaring mutable state that is shared across the program. Classes, however, are notoriously difficult to correctly use within concurrent programs, requiring error-prone manual synchronization to avoid data races. We want to provide the ability to use shared mutable state while still providing static detection of data races and other common concurrency bugs.
-->

<p>Swift 包含了 class，它提供了一种机制来声明整个程序共享的 mutable 状态。然而，class 在并发程序中难以正确使用，需要手动同步来避免 data race，很容易出错。我们希望提供这么一种功能，在使用共享的 mutable 状态的同时，仍然提供对 data race 和其他常见并发 bug 的静态检测。</p>
<!--
The [actor model](https://en.wikipedia.org/wiki/Actor_model) defines entities called *actors* that are perfect for this task. Actors allow you as a programmer to declare that a bag of state is held within a concurrency domain and then define multiple operations that act upon it. Each actor protects its own data through *data isolation*, ensuring that only a single thread will access that data at a given time, even when many clients are concurrently making requests of the actor. As part of the Swift Concurrency Model, actors provide the same race and memory safety properties as structured concurrency, but provide the familiar abstraction and reuse features that other explicitly declared types in Swift enjoy.
-->

<p><a href="https://en.wikipedia.org/wiki/Actor_model">Actor 模型</a>定义了称为 <em>actors</em> 的实体，这些实体非常适合这项任务。Actor 允许你，作为一个程序员，声明一系列的状态，这些状态由一个 concurrency domain 持有，并且可以定义多个对其的操作。每个 actor 通过 <em>data isolation</em> 来保护自己的数据，确保在给定时间内只有一个线程访问它的数据，即使许多用户同时向 actor 发出请求。作为 Swift 并发模型的一部分，actor 提供了与结构化并发相同的 race 和内存安全属性，但也提供了 Swift 中其他显式声明的类型所享有的熟悉的抽象和复用的特性。</p>]]>
    </summary>
    <title>【译】SE-0306 Actors</title>
    <updated>2026-09-02T11:08:19.194Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<blockquote><p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md">SE-0300 Continuations for interfacing async tasks with synchronous code</a></p></blockquote><ul><li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md">SE-0300</a></li><li>Authors: <a href="https://github.com/rjmccall">John McCall</a>, <a href="https://github.com/jckarter">Joe Groff</a>, <a href="https://github.com/DougGregor">Doug Gregor</a>, <a href="https://github.com/ktoso">Konrad Malawski</a></li><li>Review Manager: <a href="https://github.com/airspeedswift">Ben Cohen</a></li><li>Status: <strong>Implemented</strong></li><li>Previous Revisions: <a href="https://github.com/apple/swift-evolution/blob/5f79481244329ec2860951c0c49c101aef5069e7/proposals/0300-continuation.md">1</a>, <a href="https://github.com/apple/swift-evolution/blob/61c788cdb9674c99fc8731b49056cebcb5497edd/proposals/0300-continuation.md">2</a></li></ul><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--Asynchronous Swift code needs to be able to work with existing synchronous code that uses techniques such as completion callbacks and delegate methods to respond to events. Asynchronous tasks can suspend themselves on **continuations** which synchronous code can then capture and invoke to resume the task in response to an event.--><p>异步 Swift代 码需要能够与现有的同步代码一起工作，这些代码使用回调和 delegate 等方式来响应事件。异步任务可以在 <strong>continuations</strong> 上暂停自己，然后同步代码可以捕获并调用它来恢复任务以响应事件。</p><p>Swift-evolution thread:</p><ul><li><a href="https://forums.swift.org/t/concurrency-structured-concurrency/41622">Structured concurrency</a></li><li><a href="https://forums.swift.org/t/concurrency-continuations-for-interfacing-async-tasks-with-synchronous-code/43619">Continuations for interfacing async tasks with synchronous code</a></li></ul><span id="more"></span><h2 id="动机"><a href="#动机" class="headerlink" title="动机"></a>动机</h2><!--Swift APIs often provide asynchronous code execution by way of a callback. This may occur either because the code itself was written prior to the introduction of async/await, or (more interestingly in the long term) because it ties in with some other system that is primarily event-driven. In such cases, one may want to provide an async interface to clients while using callbacks internally.  In these cases, the calling async task needs to be able to suspend itself, while providing a mechanism for the event-driven synchronous system to resume it in response to an event.--><p>Swift 的 API 经常会通过回调的方式提供异步执行的功能。这种情况可能是因为代码本身是在引入 async&#x2F;await 之前编写的，或者（更有趣的是，从长远来看）是因为它与其它一些（主要是）事件驱动的系统联系在一起。在这种情况下，人们可能希望向客户端提供一个异步接口，同时在内部使用回调。在这些情况下，调用异步任务需要能够暂停自己，同时提供一个机制让事件驱动的同步系统在响应事件时恢复它。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><!--The library will provide APIs to get a **continuation** for the current asynchronous task. Getting the task's continuation suspends the task, and produces a value that synchronous code can then use a handle to resume the task. Given a completion callback based API like:--><p>本提案将提供 API 来获取当前异步任务的 <strong>continuation</strong>。获取任务的 continuation 会暂停任务，并产生一个值，同步的代码可以使用 handle 来恢复任务。给定一个基于 completionHandler 的 API，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">beginOperation</span>(<span class="params">completion</span>: (<span class="type">OperationResult</span>) -&gt; <span class="type">Void</span>)</span><br></pre></td></tr></table></figure><!--we can turn it into an `async` interface by suspending the task and using its continuation to resume it when the callback is invoked, turning the argument passed into the callback into the normal return value of the async function:--><p>我们可以把它变成一个 <code>async</code> 接口，先将当前的任务暂停，然后把它的 continuation 传入闭包中用来恢复任务，把传入回调的参数作为 async 函数的返回值：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">operation</span>() <span class="keyword">async</span> -&gt; <span class="type">OperationResult</span> &#123;</span><br><span class="line">  <span class="comment">// 暂停当前任务，立即执行这个闭包，并且将它的 contunation 传进去</span></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">await</span> withUnsafeContinuation &#123; continuation <span class="keyword">in</span></span><br><span class="line">    <span class="comment">// 执行基于 callback 的同步 API</span></span><br><span class="line">    beginOperation(completion: &#123; result <span class="keyword">in</span></span><br><span class="line">      <span class="comment">// 当回调执行时恢复 contiuation</span></span><br><span class="line">      continuation.resume(returning: result)</span><br><span class="line">    &#125;) </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="具体设计"><a href="#具体设计" class="headerlink" title="具体设计"></a>具体设计</h2><h3 id="原始的-unsafe-continuations"><a href="#原始的-unsafe-continuations" class="headerlink" title="原始的 unsafe continuations"></a>原始的 unsafe continuations</h3><!--The library provides two functions, `withUnsafeContinuation` and `withUnsafeThrowingContinuation`, that allow one to call into a callback-based API from inside async code. Each function takes an *operation* closure, which is expected to call into the callback-based API. The closure receives a continuation instance that must be resumed by the callback, either to provide the result value or (in the throwing variant) the thrown error that becomes the result of the `withUnsafeContinuation` call when the async task resumes:--><p>本提案提供了两个函数，<code>withUnsafeContinuation</code> 和 <code>withUnsafeThrowingContinuation</code>，允许用户在异步代码内部使用基于回调的 API。这两个函数都需要传入一个 <em>operation</em> 闭包，它通常会在基于回调的 API 里调用。闭包里接收一个必须由回调恢复的 continuation 实例，用于提供结果值或（在 throw 的版本中）抛出错误，当异步任务恢复时，成为 <code>withUnsafeContinuation</code> 调用的结果：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UnsafeContinuation</span>&lt;<span class="type">T</span>, <span class="type">E</span>: <span class="type">Error</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>(<span class="params">returning</span>: <span class="type">T</span>)</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>(<span class="params">throwing</span>: <span class="type">E</span>)</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>(<span class="params">with</span> <span class="params">result</span>: <span class="type">Result</span>&lt;<span class="type">T</span>, <span class="type">E</span>&gt;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeContinuation</span> <span class="keyword">where</span> <span class="type">T</span> <span class="operator">==</span> <span class="type">Void</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>() &#123; resume(returning: ()) &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeContinuation</span> <span class="keyword">where</span> <span class="type">E</span> <span class="operator">==</span> <span class="type">Error</span> &#123;</span><br><span class="line">  <span class="comment">// 允许 Result 版本使用更严格的 Error 类型</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>&lt;<span class="type">ResultError</span>: <span class="type">Error</span>&gt;(<span class="params">with</span> <span class="params">result</span>: <span class="type">Result</span>&lt;<span class="type">T</span>, <span class="type">ResultError</span>&gt;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">withUnsafeContinuation</span>&lt;<span class="type">T</span>&gt;(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operation</span>: (<span class="type">UnsafeContinuation</span>&lt;<span class="type">T</span>, <span class="type">Never</span>&gt;) -&gt; ()</span><br><span class="line">) <span class="keyword">async</span> -&gt; <span class="type">T</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">withUnsafeThrowingContinuation</span>&lt;<span class="type">T</span>&gt;(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operation</span>: (<span class="type">UnsafeContinuation</span>&lt;<span class="type">T</span>, <span class="type">Error</span>&gt;) <span class="keyword">throws</span> -&gt; ()</span><br><span class="line">) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">T</span></span><br></pre></td></tr></table></figure><!--`withUnsafe*Continuation` will run its `operation` argument immediately in the task's current context, passing in a *continuation* value that can be used to resume the task. The `operation` function must arrange for the continuation to be resumed at some point in the future; after the `operation` function returns, the task is suspended. The task must then be brought out of the suspended state by invoking one of the continuation's `resume` methods.  Note that `resume` immediately returns control to the caller after transitioning the task out of its suspended state; the task itself does not actually resume execution until its executor reschedules it. The argument to `resume(returning:)` becomes the return value of `withUnsafe*Continuation` when the task resumes execution.  `resume(throwing:)` can be used instead to make the task resume by propagating the given error. As a convenience, given a `Result`, `resume(with:)` can be used to resume the task by returning normally or raising an error according to the state of the `Result`. If the `operation` raises an uncaught error before returning, this behaves as if the operation had invoked `resume(throwing:)` with the error.--><p><code>withUnsafe*Continuation</code> 将在任务的当前上下文中立即执行它的 <code>operation</code> 参数，传入一个可用于恢复任务的 <em>continuation</em> 值。<code>operation</code> 函数必须安排在未来的某个时刻恢复 continuation；在 <code>operation</code> 函数返回后，任务就会被暂停。然后，必须通过调用 continuation 任意一个 <code>resume</code> 方法使任务脱离暂停状态。注意，<code>resume</code> 在将任务从暂停状态过渡出来后，会立即将控制权返回给调用者；任务本身实际上并不会恢复执行，而是等到它的执行者再次调度它。当任务恢复执行时，<code>resume(returning:)</code> 的参数会成为 <code>withUnsafe*Continuation</code> 的返回值。<code>resume(throwing:)</code> 可以通过传入给定的 Error 使任务恢复。为了方便起见，可以传入一个 <code>Result</code>，<code>resume(with:)</code> 会根据 <code>Result</code> 的状态，通过正常返回或抛出错误来恢复任务。如果 <code>operation</code> 在返回前引发了一个未捕获的错误，就会跟调用了 <code>resume(throwing:)</code> 一样。</p><!--If the return type of `withUnsafe*Continuation` is `Void`, one must specify a value of `()` when calling `resume(returning:)`. Doing so produces some unsightly code, so `Unsafe*Continuation<Void>` has an extra member `resume()` that makes the function call easier to read.--><p>如果 <code>withUnsafe*Continuation</code> 的返回类型是 <code>Void</code>，那么在调用 <code>resume(returning:)</code> 时必须传入 <code>()</code>。这样做会产生一些丑陋的代码，所以 <code>Unsafe*Continuation&lt;Void&gt;</code> 有一个额外的方法 <code>resume()</code>，使调用代码更容易阅读。</p><!--After invoking `withUnsafeContinuation`, exactly one `resume` method must be called *exactly-once* on every execution path through the program.  `Unsafe*Continuation` is an unsafe interface, so it is undefined behavior if a `resume` method is invoked on the same continuation more than once. The task remains in the suspended state until it is resumed; if the continuation is discarded and never resumed, then the task will be left suspended until the process ends, leaking any resources it holds.  Wrappers can provide checking for these misuses of continuations, and the library will provide one such wrapper, discussed below.--><p>在调用 <code>withUnsafeContinuation</code> 后，每个分支上都必须<strong>调用一次且仅一次</strong> <code>resume</code> 方法。<code>Unsafe*Continuation</code> 是一个不安全的接口，所以同一个 continuation 多次调用 <code>resume</code> 方法属于未定义的行为。在任务被恢复之前，它会一直处于暂停状态；如果 continuation 被释放掉了，并且从未被恢复，那么任务将一直处于暂停状态，直到进程结束，它所拥有的任何资源都会泄漏。我们可以提供一层封装捕获这些错误的使用，本提案也打算引入这样的一个 Wrapper，下面将详细讨论。</p><!--Using the `Unsafe*Continuation` API, one may for example wrap such (purposefully convoluted for the sake of demonstrating the flexibility of the continuation API) function:--><p>例如，使用 <code>Unsafe*Continuation</code> API，可以封装这样的函数（这里为了展示 continuation API 的灵活性而故意弄得很复杂）：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">buyVegetables</span>(</span><br><span class="line">  <span class="params">shoppingList</span>: [<span class="type">String</span>],</span><br><span class="line">  <span class="comment">// a) if all veggies were in store, this is invoked *exactly-once*</span></span><br><span class="line">  <span class="params">onGotAllVegetables</span>: ([<span class="type">Vegetable</span>]) -&gt; (),</span><br><span class="line"></span><br><span class="line">  <span class="comment">// b) if not all veggies were in store, invoked one by one *one or more times*</span></span><br><span class="line">  <span class="params">onGotVegetable</span>: (<span class="type">Vegetable</span>) -&gt; (),</span><br><span class="line">  <span class="comment">// b) if at least one onGotVegetable was called *exactly-once*</span></span><br><span class="line">  <span class="comment">//    this is invoked once no more veggies will be emitted</span></span><br><span class="line">  <span class="params">onNoMoreVegetables</span>: () -&gt; (),</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// c) if no veggies _at all_ were available, this is invoked *exactly once*</span></span><br><span class="line">  <span class="params">onNoVegetablesInStore</span>: (<span class="type">Error</span>) -&gt; ()</span><br><span class="line">)</span><br><span class="line"><span class="comment">// returns 1 or more vegetables or throws an error</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">buyVegetables</span>(<span class="params">shoppingList</span>: [<span class="type">String</span>]) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; [<span class="type">Vegetable</span>] &#123;</span><br><span class="line">  <span class="keyword">try</span> <span class="keyword">await</span> withUnsafeThrowingContinuation &#123; continuation <span class="keyword">in</span></span><br><span class="line">    <span class="keyword">var</span> veggies: [<span class="type">Vegetable</span>] <span class="operator">=</span> []</span><br><span class="line"></span><br><span class="line">    buyVegetables(</span><br><span class="line">      shoppingList: shoppingList,</span><br><span class="line">      onGotAllVegetables: &#123; veggies <span class="keyword">in</span> continuation.resume(returning: veggies) &#125;,</span><br><span class="line">      onGotVegetable: &#123; v <span class="keyword">in</span> veggies.append(v) &#125;,</span><br><span class="line">      onNoMoreVegetables: &#123; continuation.resume(returning: veggies) &#125;,</span><br><span class="line">      onNoVegetablesInStore: &#123; error <span class="keyword">in</span> continuation.resume(throwing: error) &#125;,</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> veggies <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> buyVegetables(shoppingList: [<span class="string">&quot;onion&quot;</span>, <span class="string">&quot;bell pepper&quot;</span>])</span><br></pre></td></tr></table></figure><!--Thanks to weaving the right continuation resume calls into the complex callbacks of the `buyVegetables` function, we were able to offer a much nicer overload of this function, allowing async code to interact with this function in a more natural straight-line way.--><p>由于在 <code>buyVegetables</code> 函数的复杂回调里正确地对 continuation resume 进行了调用，我们能够为这个函数提供一个更好的重载，让异步代码以更自然的方式与这个函数交互。</p><h3 id="Checked-continuations"><a href="#Checked-continuations" class="headerlink" title="Checked continuations"></a>Checked continuations</h3><!--`Unsafe*Continuation` provides a lightweight mechanism for interfacing sync and async code, but it is easy to misuse, and misuse can corrupt the process state in dangerous ways. In order to provide additional safety and guidance when developing interfaces between sync and async code, the library will also provide a wrapper which checks for invalid use of the continuation:--><p><code>Unsafe*Continuation</code> 为同步和异步代码的接口提供了一个轻量的机制，但它很容易用错，并且会以危险的方式破坏进程状态。为了提供额外的安全性和指导（在开发同步和异步代码交互的接口时），本提案还将提供一个 Wrapper，用于检查对 continuation 的非法使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">CheckedContinuation</span>&lt;<span class="type">T</span>, <span class="type">E</span>: <span class="type">Error</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>(<span class="params">returning</span>: <span class="type">T</span>)</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>(<span class="params">throwing</span>: <span class="type">E</span>)</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>(<span class="params">with</span> <span class="params">result</span>: <span class="type">Result</span>&lt;<span class="type">T</span>, <span class="type">E</span>&gt;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">CheckedContinuation</span> <span class="keyword">where</span> <span class="type">T</span> <span class="operator">==</span> <span class="type">Void</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">CheckedContinuation</span> <span class="keyword">where</span> <span class="type">E</span> <span class="operator">==</span> <span class="type">Error</span> &#123;</span><br><span class="line">  <span class="comment">// Allow covariant use of a `Result` with a stricter error type than</span></span><br><span class="line">  <span class="comment">// the continuation:</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">resume</span>&lt;<span class="type">ResultError</span>: <span class="type">Error</span>&gt;(<span class="params">with</span> <span class="params">result</span>: <span class="type">Result</span>&lt;<span class="type">T</span>, <span class="type">ResultError</span>&gt;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">withCheckedContinuation</span>&lt;<span class="type">T</span>&gt;(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operation</span>: (<span class="type">CheckedContinuation</span>&lt;<span class="type">T</span>, <span class="type">Never</span>&gt;) -&gt; ()</span><br><span class="line">) <span class="keyword">async</span> -&gt; <span class="type">T</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">withCheckedThrowingContinuation</span>&lt;<span class="type">T</span>&gt;(</span><br><span class="line">  <span class="keyword">_</span> <span class="params">operation</span>: (<span class="type">CheckedContinuation</span>&lt;<span class="type">T</span>, <span class="type">Error</span>&gt;) <span class="keyword">throws</span> -&gt; ()</span><br><span class="line">) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">T</span></span><br></pre></td></tr></table></figure><!--The API is intentionally identical to the `Unsafe` variants, so that code can switch easily between the checked and unchecked variants. For instance, the `buyVegetables` example above can opt into checking merely by turning its call of `withUnsafeThrowingContinuation` into one of `withCheckedThrowingContinuation`:--><p>这里的 API 特意与 <code>Unsafe</code> 的 API 保持一致，因此代码可以很容易地在 checked 和 unsafe 的版本之间切换。例如，上面的 <code>buyVegetables</code> 例子只需将 <code>withUnsafeThrowingContinuation</code> 的调用变成 <code>withCheckedThrowingContinuation</code> 的调用，就可以提供运行时的检查：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// returns 1 or more vegetables or throws an error</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">buyVegetables</span>(<span class="params">shoppingList</span>: [<span class="type">String</span>]) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; [<span class="type">Vegetable</span>] &#123;</span><br><span class="line">  <span class="keyword">try</span> <span class="keyword">await</span> withCheckedThrowingContinuation &#123; continuation <span class="keyword">in</span></span><br><span class="line">    <span class="keyword">var</span> veggies: [<span class="type">Vegetable</span>] <span class="operator">=</span> []</span><br><span class="line"></span><br><span class="line">    buyVegetables(</span><br><span class="line">      shoppingList: shoppingList,</span><br><span class="line">      onGotAllVegetables: &#123; veggies <span class="keyword">in</span> continuation.resume(returning: veggies) &#125;,</span><br><span class="line">      onGotVegetable: &#123; v <span class="keyword">in</span> veggies.append(v) &#125;,</span><br><span class="line">      onNoMoreVegetables: &#123; continuation.resume(returning: veggies) &#125;,</span><br><span class="line">      onNoVegetablesInStore: &#123; error <span class="keyword">in</span> continuation.resume(throwing: error) &#125;,</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Instead of leading to undefined behavior, `CheckedContinuation` will instead trap if the program attempts to resume the continuation multiple times.  `CheckedContinuation` will also log a warning if the continuation is discarded without ever resuming the task, which leaves the task stuck in its suspended state, leaking any resources it holds. These checks happen regardless of the optimization level of the program.--><p><code>CheckedContinuation</code> 不会导致未定义的行为，相反，如果程序试图多次 resume continuation 的话，<code>CheckedContinuation</code> 就会捕获到这种情况并且触发 trap。如果在没有恢复任务的情况下释放了 continuation，这使任务停留在它的暂停状态，泄露它所拥有的任何资源，并且 <code>CheckedContinuation</code> 会打印一个警告。无论程序采用了哪种优化级别，这些检查都会执行。</p><h2 id="更多例子"><a href="#更多例子" class="headerlink" title="更多例子"></a>更多例子</h2><!--Continuations can be used to interface with more complex event-driven interfaces than callbacks as well. As long as the entirety of the process follows the requirement that the continuation be resumed exactly once, there are no other restrictions on where the continuation can be resumed. For instance, an `Operation` implementation can trigger resumption of a continuation when the operation completes:--><p>Continuation 也可以用来跟（比回调）更复杂的事件驱动接口对接。只要整个过程都符合要求（continuation 只被恢复一次），那么在哪里恢复 continuation 就没有其它限制。例如，一个 <code>Operation</code> 可以在操作完成时触发 continuation 的恢复：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyOperation</span>: <span class="title class_ inherited__">Operation</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> continuation: <span class="type">UnsafeContinuation</span>&lt;<span class="type">OperationResult</span>, <span class="type">Never</span>&gt;</span><br><span class="line">  <span class="keyword">var</span> result: <span class="type">OperationResult</span>?</span><br><span class="line"></span><br><span class="line">  <span class="keyword">init</span>(<span class="params">continuation</span>: <span class="type">UnsafeContinuation</span>&lt;<span class="type">OperationResult</span>, <span class="type">Never</span>&gt;) &#123;</span><br><span class="line">    <span class="keyword">self</span>.continuation <span class="operator">=</span> continuation</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/* rest of operation populates `result`... */</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">finish</span>() &#123;</span><br><span class="line">    continuation.resume(returning: result<span class="operator">!</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">doOperation</span>() <span class="keyword">async</span> -&gt; <span class="type">OperationResult</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">await</span> withUnsafeContinuation &#123; continuation <span class="keyword">in</span></span><br><span class="line">    <span class="type">MyOperation</span>(continuation: continuation).start()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Using APIs from the [structured concurrency proposal](https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md), one can wrap up a `URLSession` in a task, allowing the task's cancellation to control cancellation of the session, and using a continuation to respond to data and error events fired by the network activity:--><p>使用<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发提案</a>中的 API，可以将一个 <code>URLSession</code> 包裹在一个任务中，让任务的取消来控制 session 的取消，并使用 continuation 来响应网络请求接收的数据和抛出的错误：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">download</span>(<span class="params">url</span>: <span class="type">URL</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Data</span>? &#123;</span><br><span class="line">  <span class="keyword">var</span> urlSessionTask: <span class="type">URLSessionTask</span>?</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">try</span> <span class="type">Task</span>.withCancellationHandler &#123;</span><br><span class="line">    urlSessionTask<span class="operator">?</span>.cancel()</span><br><span class="line">  &#125; operation: &#123;</span><br><span class="line">    <span class="keyword">let</span> result: <span class="type">Data</span>? <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> withUnsafeThrowingContinuation &#123; continuation <span class="keyword">in</span></span><br><span class="line">      urlSessionTask <span class="operator">=</span> <span class="type">URLSession</span>.shared.dataTask(with: url) &#123; data, <span class="keyword">_</span>, error <span class="keyword">in</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">case</span> (<span class="keyword">let</span> cancelled <span class="keyword">as</span> <span class="type">NSURLErrorCancelled</span>)<span class="operator">?</span> <span class="operator">=</span> error &#123;</span><br><span class="line">          continuation.resume(returning: <span class="literal">nil</span>)</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> <span class="keyword">let</span> error <span class="operator">=</span> error &#123;</span><br><span class="line">          continuation.resume(throwing: error)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          continuation.resume(returning: data)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      urlSessionTask<span class="operator">?</span>.resume()</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">let</span> result <span class="operator">=</span> result &#123;</span><br><span class="line">      <span class="keyword">return</span> result</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="type">Task</span>.cancel()</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--It is also possible for wrappers around callback based APIs to respect their parent/current tasks's cancellation, as follows:--><p>围绕基于回调的 API 的 Wrapper 也可以跟随其父级&#x2F;当前任务的取消，如下所示。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">fetch</span>(<span class="params">items</span>: <span class="type">Int</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; [<span class="type">Items</span>] &#123;</span><br><span class="line">  <span class="keyword">let</span> worker <span class="operator">=</span> <span class="operator">...</span> </span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">try</span> <span class="type">Task</span>.withCancellationHandler(</span><br><span class="line">    handler: &#123; worker<span class="operator">?</span>.cancel() &#125;</span><br><span class="line">  ) &#123; </span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">try</span> <span class="keyword">await</span> withUnsafeThrowingContinuation &#123; c <span class="keyword">in</span> </span><br><span class="line">      worker.work(</span><br><span class="line">        onNext: &#123; value <span class="keyword">in</span> c.resume(returning: value) &#125;,</span><br><span class="line">        onCancelled: &#123; value <span class="keyword">in</span> c.resume(throwing: <span class="type">CancellationError</span>()) &#125;,</span><br><span class="line">      )</span><br><span class="line">    &#125; </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--If tasks were allowed to have instances, which is under discussion in the structured concurrency proposal, it would also be possible to obtain the task in which the fetch(items:) function was invoked and call isCanceled on it whenever the insides of the withUnsafeThrowingContinuation would deem it worthwhile to do so.--><p>如果允许任务拥有实例（这是在结构化并发提案里讨论的内容），那么也可以获得调用 <code>fetch(items:)</code> 函数的任务，并在 <code>withUnsafeThrowingContinuation</code> 内部认为需要时，调用它的 <code>isCanceled</code>。</p><h2 id="其它方案"><a href="#其它方案" class="headerlink" title="其它方案"></a>其它方案</h2><h3 id="命名缩短为-Continuation（而不是-CheckedContinuation）"><a href="#命名缩短为-Continuation（而不是-CheckedContinuation）" class="headerlink" title="命名缩短为 Continuation（而不是 CheckedContinuation）"></a>命名缩短为 <code>Continuation</code>（而不是 <code>CheckedContinuation</code>）</h3><!--We could position `CheckedContinuation` as the "default" API for doing sync/async interfacing by leaving the `Checked` word out of the name. This would certainly be in line with the general philosophy of Swift that safe interfaces are preferred, and unsafe ones used selectively where performance is an overriding concern. There are a couple of reasons to hesitate at doing this here, though:--><p>我们可以将 <code>CheckedContinuation</code> 定位为做同步&#x2F;异步接口”默认”的 API，把 <code>Checked</code> 这个词从名字中去掉。这符合 Swift 的理念，首选安全的接口，而不安全的接口是性能成为首要考虑因素时才会选择性地使用。不过，这里有几个理由让我们很犹豫：</p><!--- Although the consequences of misusing `CheckedContinuation` are not as severe as `UnsafeContinuation`, it still only does a best effort at checking for some common misuse patterns, and it does not render the consequences of continuation misuse entirely moot: dropping a continuation without resuming it will still leak the un-resumed task, and attempting to resume a continuation multiple times will still cause the information passed through the continuation to be lost. It is still a serious programming error if a `with*Continuation` operation misuses the continuation; `CheckedContinuation` only helps make the error more apparent.- Naming a type `Continuation` now might take the "good" name away if, after we have move-only types at some point in the future, we want to introduce a continuation type that statically enforces the exactly-once property.--><ul><li>虽然错误使用 <code>CheckedContinuation</code> 的后果没有 <code>UnsafeContinuation</code> 那么严重，但它仍然只是尽最大努力检查一些常见的错误模式，并不能掩盖掉错误使用 continuation 的后果：释放一个 continuation 而不恢复它仍然会泄露未恢复的任务，而试图多次恢复一个 continuation 仍然会导致通过 continuation 传递的信息丢失。如果 <code>with*Continuation</code> 的操作错误使用 continuation，仍然是一个严重的程序逻辑错误； <code>CheckedContinuation</code> 只能使错误变得更加明显。</li><li>如果我们在未来的某个时候有了 move-only 类型，我们想引入一个静态的只恢复 exactly-once 的 continuation 类型，那么现在命名一个类型 <code>Continuation</code> 会占用掉这个”好”名字。</li></ul><h3 id="不暴露-UnsafeContinuation-接口"><a href="#不暴露-UnsafeContinuation-接口" class="headerlink" title="不暴露 UnsafeContinuation 接口"></a>不暴露 <code>UnsafeContinuation</code> 接口</h3><!--One could similarly make an argument that `UnsafeContinuation` shouldn't be exposed at all, since the `Checked` form can always be used instead. We think that being able to avoid the cost of checking when interacting with performance-sensitive APIs is valuable, once users have validated that their interfaces to those APIs are correct.--><p>同样的，我们也可以得出另一个论点，即 <code>UnsafeContinuation</code> 根本不应该暴露给上层用户，因为用户总是会使用 <code>Checked</code> 的版本。但我们认为，在与性能敏感的 API 交互时，一旦用户验证了他们调用这些 API 的方式是正确的，避免掉 checked 的成本就是有价值的。</p><h3 id="让-CheckedContinuation-在错误使用时全部触发-trap-或者全部-log-出来"><a href="#让-CheckedContinuation-在错误使用时全部触发-trap-或者全部-log-出来" class="headerlink" title="让 CheckedContinuation 在错误使用时全部触发 trap, 或者全部 log 出来"></a>让 <code>CheckedContinuation</code> 在错误使用时全部触发 trap, 或者全部 log 出来</h3><!--`CheckedContinuation` is proposed to trap when the program attempts to resume the same continuation twice, but only log a warning if a continuation is abandoned without getting resumed. We think this is the right tradeoff for these different situations for the following reasons:--><p><code>CheckedContinuation</code> 的目的是当程序试图多次恢复同一个 continuation 时捕获这种情况，但如果一个 continuation 被释放而没有得到恢复，则只 log 一个警告。我们认为这是对不同情况的正确权衡，原因如下：</p><!--- With `UnsafeContinuation`, resuming multiple times corrupts the process and leaves it in an undefined state. By trapping when the task is resumed multiple times, `CheckedContinuation` turns undefined behavior into a well- defined trap situation.  This is analogous to other checked/unchecked pairings in the standard library, such as `!` vs. `unsafelyUnwrapped` for `Optional`.- By contrast, failing to resume a continuation with `UnsafeContinuation` does not corrupt the task, beyond leaking the suspended task's resources; the rest of the program can continue executing normally. Furthermore, the only way we can currently detect and report such a leak is by using a class `deinit` in its implementation. The precise moment at which such a deinit would execute is not entirely predictable because of refcounting variability from ARC optimization. If `deinit` were made to trap, whether that trap is executed and when could vary with optimization level, which we don't think would lead to a good experience.--><ul><li>对于 <code>UnsafeContinuation</code>，多次恢复会破坏进程，使其处于<strong>未定义状态</strong>。通过在任务多次恢复时触发 trap，<code>CheckedContinuation</code> 就会将未定义的行为变成了定义良好的 trap。这类似于标准库中其它的 checked&#x2F;unchecked 配对，例如 <code>!</code> 与 <code>Optional</code> 的 <code>unsafelyUnwrapped</code>。</li><li>相比之下，如果没有用 <code>UnsafeContinuation</code> 来恢复 continuation，除了泄露暂停的任务的资源之外，并不会破坏任务，程序的其它部分也可以继续正常执行。此外，目前我们能检测和报告这种泄漏的唯一方法是在其实现中使用类的 <code>deinit</code>。由于 ARC 优化的 refcounting 变化，这样 deinit 执行的准确时机是完全无法预测的。如果把 <code>deinit</code> 做成 trap，那么这个 trap 是否被执行以及何时执行可能会随着优化等级的变化而变化，我们认为这不会带来很好的体验。</li></ul><h3 id="让-Continuation-提供更多-Task-API，或者允许-continuation-恢复-Handle"><a href="#让-Continuation-提供更多-Task-API，或者允许-continuation-恢复-Handle" class="headerlink" title="让 *Continuation 提供更多 Task API，或者允许 continuation 恢复 Handle"></a>让 <code>*Continuation</code> 提供更多 <code>Task</code> API，或者允许 continuation 恢复 <code>Handle</code></h3><!--The full `Task` and `Handle` API provides additional control over the task state to holders of the handle, particularly the ability to query and set cancellation state, as well as await the final result of the task, and one might wonder why the `*Continuation` types do not also expose this functionality.  The role of a `Continuation` is very different from a `Handle`, in that a handle represents and controls the entire lifetime of the task, whereas a continuation only represents a *single suspension point* in the lifetime of the task.  Furthermore, the `*Continuation` API is primarily designed to allow for interfacing with code outside of Swift's structured concurrency model, and we believe that interactions between tasks are best handled inside that model as much as possible.--><p>完整的 <code>Task</code> 和 <code>Handle</code> API 为 Handle 的持有者提供了对任务状态的额外控制，特别是查询和设置取消状态，以及等待任务最终结果的能力，大家可能会问，为什么 <code>*Continuation</code> 类型没有这些功能？<code>Continuation</code> 与 <code>Handle</code> 的作用有很大的不同，Handle 代表和控制任务的整个生命周期，而 continuation 只代表任务生命期中的<strong>一个暂停点</strong>。此外，<code>*Continuation</code> API 的设计主要是为了与 Swift 结构化并发模型之外的代码进行对接，我们认为任务之间的交互最好还是尽量在这个模型里处理。</p><!--Note that `*Continuation` also does not strictly need direct support for any task API on itself. If, for instance, someone wants a task to cancel itself in response to a callback, they can achieve that by funneling a sentinel through the continuation's resume type, such as an Optional's `nil`:--><p>注意，<code>*Continuation</code> 其实也不需要自己去直接提供任何任务的 API。例如，如果有人想让一个任务在响应回调时自行取消，他们可以通过控制 continuation 的 resume 类型来实现（例如一个 Optional 的 <code>nil</code>）：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> callbackResult: <span class="type">Result</span>? <span class="operator">=</span> <span class="keyword">await</span> withUnsafeContinuation &#123; c <span class="keyword">in</span></span><br><span class="line">  someCallbackBasedAPI(</span><br><span class="line">    completion: &#123; c.resume(<span class="variable">$0</span>) &#125;,</span><br><span class="line">    cancellation: &#123; c.resume(<span class="literal">nil</span>) &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">let</span> result <span class="operator">=</span> callbackResult &#123;</span><br><span class="line">  process(result)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  cancel()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="提供-API-立刻恢复任务，避免队列跳转"><a href="#提供-API-立刻恢复任务，避免队列跳转" class="headerlink" title="提供 API 立刻恢复任务，避免队列跳转"></a>提供 API 立刻恢复任务，避免队列跳转</h3><!--Some APIs, in addition to taking a completion handler or delegate, also allow the client to control *where* that completion handler or delegate's methods are invoked; for instance, some APIs on Apple platforms take an argument for the dispatch queue the completion handler should be invoked by. In these cases, it would be optimal if the original API could resume the task directly on the dispatch queue (or whatever other scheduling mechanism, such as a thread or run loop) that the task would normally be resumed on by its executor. To enable this, we could provide a variant of `with*Continuation` that, in addition to providing a continuation, also provides the dispatch queue that the task expects to be resumed on. The `*Continuation` type in turn could provide an `unsafeResumeImmediately` set of APIs, which would immediately resume execution of the task on the current thread. This would enable something like this:--><p>有些 API 除了接收一个 completionHandler 或 delegate，还允许客户端控制 completionHandler 或 delegate 的方法调用的位置；例如，Apple 平台上的一些 API 接收一个参数，作为 completionHandler 应该被使用的 DispatchQueue。在这些情况下，如果原始 API 能够直接在 DispatchQueue（或其它任何调度机制，如 Thread 或 Runloop）上恢复任务，那将是最理想的。为了实现这一点，我们可以提供一个 <code>with*Continuation</code> 的变体，除了提供一个 continuation 之外，还提供任务期望被恢复的 DispatchQueue。<code>*Continuation</code> 类型也可以提供一套 <code>unsafeResumeImmediately</code> 的 API，它将立即在当前线程上恢复任务的执行。这样就可以实现下面的功能：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Given an API that takes a queue and completion handler:</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">doThingAsynchronously</span>(<span class="params">queue</span>: <span class="type">DispatchQueue</span>, <span class="params">completion</span>: (<span class="type">ResultType</span>) -&gt; <span class="type">Void</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// We could wrap it in a Swift async function like:</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">doThing</span>() <span class="keyword">async</span> -&gt; <span class="type">ResultType</span> &#123;</span><br><span class="line">  <span class="keyword">await</span> withUnsafeContinuationAndCurrentDispatchQueue &#123; c, queue <span class="keyword">in</span></span><br><span class="line">    <span class="comment">// Schedule to resume on the right queue, if we know it</span></span><br><span class="line">    doThingAsynchronously(queue: queue) &#123;</span><br><span class="line">      c.unsafeResumeImmediately(returning: <span class="variable">$0</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--However, such an API would have to be used very carefully; the programmer would have to be careful that `unsafeResumeImmediately` is in fact invoked in the correct context, and that it is safe to take over control of the current thread from the caller for a potentially unbounded amount of time.  If the task is resumed in the wrong context, it will break assumptions in the written code as well as those made by the compiler and runtime, which will lead to subtle bugs that would be difficult to diagnose. We can investigate this as an addition to the core proposal, if "queue hopping" in continuation- based adapters turns out to be a performance problem in practice.--><p>然而，这样的 API 必须非常谨慎地使用；程序员必须保证 <code>unsafeResumeImmediately</code> 在正确的上下文中被调用，并且在任意时间内，从调用者手中接管当前线程的行为必须保证是安全的。如果在错误的上下文中恢复任务，就会破坏代码逻辑以及编译器和运行时的假设，这将导致难以诊断的奇妙 bug。如果 continuation-based adapter 的”队列跳转”在实践中被证实是一个性能问题，我们可以将其作为核心提案的补充进行研究。</p><h2 id="修订历史"><a href="#修订历史" class="headerlink" title="修订历史"></a>修订历史</h2><p>Third revision:</p><ul><li>Replaced separate <code>*Continuation&lt;T&gt;</code> and <code>*ThrowingContinuation&lt;T&gt;</code> types with a single <code>Continuation&lt;T, E: Error&gt;</code> type parameterized on the error type.</li><li>Added a convenience <code>resume()</code> equivalent to <code>resume(returning: ())</code> for continuations with a <code>Void</code> return type.</li><li>Changed <code>with*ThrowingContinuation</code> to take an <code>operation</code> block that may throw, and to immediately resume the task throwing the error if an uncaught error propagates from the operation.</li></ul><p>Second revision:</p><ul><li>Clarified the execution behavior of <code>with*Continuation</code> and <code>*Continuation.resume</code>, namely that <code>with*Continuation</code> immediately executes its operation argument in the current context before suspending the task, and that <code>resume</code> immediately returns to its caller after un-suspending the task, leaving the task to be scheduled by its executor.</li><li>Removed an unnecessary invariant on when <code>resume</code> must be invoked; it is valid to invoke it exactly once at any point after the <code>with*Continuation</code> operation has started executing; it does not need to run exactly when the operation returns.</li><li>Added “future direction” discussion of a potential more advanced API that could allow continuations to directly resume their task when the correct dispatch queue to do so is known.</li><li>Added <code>resume()</code> on <code>Void</code>-returning <code>Continuation</code> types.</li></ul>]]>
    </content>
    <id>https://kemchenj.github.io/2021-03-31/</id>
    <link href="https://kemchenj.github.io/2021-03-31/"/>
    <published>2021-03-30T16:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md">SE-0300 Continuations for interfacing async tasks with synchronous code</a></p>
</blockquote>
<ul>
<li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0300-continuation.md">SE-0300</a></li>
<li>Authors: <a href="https://github.com/rjmccall">John McCall</a>, <a href="https://github.com/jckarter">Joe Groff</a>, <a href="https://github.com/DougGregor">Doug Gregor</a>, <a href="https://github.com/ktoso">Konrad Malawski</a></li>
<li>Review Manager: <a href="https://github.com/airspeedswift">Ben Cohen</a></li>
<li>Status: <strong>Implemented</strong></li>
<li>Previous Revisions: <a href="https://github.com/apple/swift-evolution/blob/5f79481244329ec2860951c0c49c101aef5069e7/proposals/0300-continuation.md">1</a>, <a href="https://github.com/apple/swift-evolution/blob/61c788cdb9674c99fc8731b49056cebcb5497edd/proposals/0300-continuation.md">2</a></li>
</ul>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--
Asynchronous Swift code needs to be able to work with existing synchronous code that uses techniques such as completion callbacks and delegate methods to respond to events. Asynchronous tasks can suspend themselves on **continuations** which synchronous code can then capture and invoke to resume the task in response to an event.
-->

<p>异步 Swift代 码需要能够与现有的同步代码一起工作，这些代码使用回调和 delegate 等方式来响应事件。异步任务可以在 <strong>continuations</strong> 上暂停自己，然后同步代码可以捕获并调用它来恢复任务以响应事件。</p>
<p>Swift-evolution thread:</p>
<ul>
<li><a href="https://forums.swift.org/t/concurrency-structured-concurrency/41622">Structured concurrency</a></li>
<li><a href="https://forums.swift.org/t/concurrency-continuations-for-interfacing-async-tasks-with-synchronous-code/43619">Continuations for interfacing async tasks with synchronous code</a></li>
</ul>]]>
    </summary>
    <title>【译】SE-0300 Continuation -- 执行同步代码的异步任务接口</title>
    <updated>2026-09-02T11:08:19.194Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<blockquote><p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md">SE-0298 Async&#x2F;Await: Sequences</a></p></blockquote><ul><li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md">SE-0298</a></li><li>Authors: <a href="https://github.com/parkera">Tony Parker</a>, <a href="https://github.com/phausler">Philippe Hausler</a></li><li>Review Manager: <a href="https://github.com/DougGregor">Doug Gregor</a></li><li>Status: <strong>Implemented (Swift 5.5)</strong></li><li>Implementation: <a href="https://github.com/apple/swift/pull/35224">apple&#x2F;swift#35224</a></li><li>Decision Notes: <a href="https://forums.swift.org/t/accepted-with-modification-se-0298-async-await-sequences/44231">Rationale</a></li><li>Revision: Based on <a href="https://forums.swift.org/t/pitch-clarify-end-of-iteration-behavior-for-asyncsequence/45548">forum discussion</a></li></ul><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--Swift's [async/await](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md) feature provides an intuitive, built-in way to write and use functions that return a single value at some future point in time. We propose building on top of this feature to create an intuitive, built-in way to write and use functions that return many values over time.--><p>Swift 的 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md">async&#x2F;await</a> 特性提供了一种直观的、内建的方式来编写和使用在未来某个时间点返回一个值的函数。我们建议在这个特性的基础上，添加一种直观的、内置的方式来编写和使用在一段时间内返回多个值的函数。</p><!--This proposal is composed of the following pieces:--><p>本提案由以下三个部分组成：</p><!--1. A standard library definition of a protocol that represents an asynchronous sequence of values2. Compiler support to use `for...in` syntax on an asynchronous sequence of values3. A standard library implementation of commonly needed functions that operate on an asynchronous sequence of values--><ol><li>增加一个表示异步序列的协议到标准库里</li><li>在异步序列上使用 <code>for...in</code> 语法的编译器支持</li><li>对异步序列进行操作的通用函数的标准库实现</li></ol><span id="more"></span><h2 id="动机"><a href="#动机" class="headerlink" title="动机"></a>动机</h2><!--We'd like iterating over asynchronous sequences of values to be as easy as iterating over synchronous sequences of values. An example use case is iterating over the lines in a file, like this:--><p>我们希望在异步序列上的遍历能够像在同步序列上的遍历一样简单。一个例子是遍历文件的每一行，像这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> <span class="keyword">try</span> <span class="keyword">await</span> line <span class="keyword">in</span> myFile.lines() &#123;</span><br><span class="line">  <span class="comment">// Do something with each line</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Using the `for...in` syntax that Swift developers are already familiar with will reduce the barrier to entry when working with asynchronous APIs. Consistency with other Swift types and concepts is therefore one of our most important goals. The requirement of using the `await` keyword in this loop will distinguish it from synchronous sequences.--><p>使用 Swift 开发者已经熟悉的 <code>for...in</code> 语法可以降低异步 API 的入门门槛。因此，保持与其他 Swift 类型和概念的一致性是我们最重要的目标之一。在这个循环中使用 <code>await</code> 关键字的可以把它与同步序列区分开来。</p><h3 id="for-in-语法"><a href="#for-in-语法" class="headerlink" title="for/in 语法"></a><code>for/in</code> 语法</h3><!--To enable the use of `for in`, we must define the return type from `func lines()` to be something that the compiler understands can be iterated. Today, we have the `Sequence` protocol. Let's try to use it here:--><p>为了实现 <code>for in</code> 语法，我们必须将 <code>func lines()</code> 的返回类型定义为可迭代的某些东西（编译器能理解的）。目前我们有 <code>Sequence</code> 协议。可以试着在这里使用它：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">URL</span> &#123;</span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">Lines</span>: <span class="title class_ inherited__">Sequence</span> &#123; <span class="comment">/* ... */</span> &#125;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">lines</span>() <span class="keyword">async</span> -&gt; <span class="type">Lines</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Unfortunately, what this function actually does is wait until *all* lines are available before returning. What we really wanted in this case was to await *each* line. While it is possible to imagine modifications to `lines` to behave differently (e.g., giving the result reference semantics), it would be better to define a new protocol to make this iteration behavior as simple as possible.--><p>不幸的是，这个函数实际上做的是等待，直到<strong>所有行</strong>都可用时才返回。在这种情况下，我们真正想要的是 await <strong>每一行</strong>。虽然可以想象对 <code>lines</code> 进行修改，使其行为不同（例如，让结果的类型变成引用语义），但最好是定义一个新的协议，使这种迭代行为尽可能简单。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">URL</span> &#123;</span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">Lines</span>: <span class="title class_ inherited__">AsyncSequence</span> &#123; <span class="comment">/* ... */</span> &#125;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">lines</span>() <span class="keyword">async</span> -&gt; <span class="type">Lines</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--`AsyncSequence` allows for waiting on each element instead of the entire result by defining an asynchronous `next()` function on its associated iterator type.--><p><code>AsyncSequence</code> 通过在其关联的迭代器类型上定义一个异步 <code>next()</code> 函数，允许对每个元素而不是整个结果进行等待。</p><h3 id="新增的-AsyncSequence-函数"><a href="#新增的-AsyncSequence-函数" class="headerlink" title="新增的 AsyncSequence 函数"></a>新增的 AsyncSequence 函数</h3><!--Going one step further, let's imagine how it might look to use our new `lines` function in more places. Perhaps we want to process lines until we reach one that is greater than a certain length.--><p>再进一步，让我们想象一下，在更多的地方使用我们新的 <code>lines</code> 函数会是什么样子。也许我们想处理每一行，直到其中一行的长度大于一定的长度。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> longLine: <span class="type">String</span>?</span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">  <span class="keyword">for</span> <span class="keyword">try</span> <span class="keyword">await</span> line <span class="keyword">in</span> myFile.lines() &#123;</span><br><span class="line">    <span class="keyword">if</span> line.count <span class="operator">&gt;</span> <span class="number">80</span> &#123;</span><br><span class="line">      longLine <span class="operator">=</span> line</span><br><span class="line">      <span class="keyword">break</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">  longLine <span class="operator">=</span> <span class="literal">nil</span> <span class="comment">// file didn&#x27;t exist</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Or, perhaps we actually do want to read all lines in the file before starting our processing:--><p>又或者，我们实际上是想在开始处理之前读取文件中的所有行：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> allLines: [<span class="type">String</span>] <span class="operator">=</span> []</span><br><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">  <span class="keyword">for</span> <span class="keyword">try</span> <span class="keyword">await</span> line <span class="keyword">in</span> myFile.lines() &#123;</span><br><span class="line">    allLines.append(line)</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">  allLines <span class="operator">=</span> []</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--There's nothing wrong with the above code, and it must be possible for a developer to write it. However, it does seem like a lot of boilerplate for what might be a common operation. One way to solve this would be to add more functions to `URL`:--><p>上面的代码没有错，开发者一定可以写出来。然而，对于普通的操作来说，它确实看起来会多很多模版代码。解决这个问题其中一个方式是在 <code>URL</code> 中增加更多的函数： </p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">URL</span> &#123;</span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">Lines</span> : <span class="title class_ inherited__">AsyncSequence</span> &#123; &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">lines</span>() -&gt; <span class="type">Lines</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">firstLongLine</span>() <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">String</span>?</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">collectLines</span>() <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; [<span class="type">String</span>]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--It doesn't take much imagination to think of other places where we may want to do similar operations, though. Therefore, we believe the best place to put these functions is instead as an extension on `AsyncSequence` itself, specified generically -- just like `Sequence`.--><p>不过，可以想象我们可能会在其他地方进行类似的操作。因此，我们认为最好是将这些函数作为 <code>AsyncSequence</code> 本身的扩展，用一种更泛用的方式 – 就像 <code>Sequence</code> 一样。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><!--The standard library will define the following protocols:--><p>标准库将会添加以下两个协议：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">AsyncSequence</span> &#123;</span><br><span class="line">  <span class="keyword">associatedtype</span> <span class="type">AsyncIterator</span>: <span class="type">AsyncIteratorProtocol</span> <span class="keyword">where</span> <span class="type">AsyncIterator</span>.<span class="type">Element</span> <span class="operator">==</span> <span class="type">Element</span></span><br><span class="line">  <span class="keyword">associatedtype</span> <span class="type">Element</span></span><br><span class="line">  __consuming <span class="keyword">func</span> <span class="title function_">makeAsyncIterator</span>() -&gt; <span class="type">AsyncIterator</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">AsyncIteratorProtocol</span> &#123;</span><br><span class="line">  <span class="keyword">associatedtype</span> <span class="type">Element</span></span><br><span class="line">  <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">next</span>() <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Element</span>?</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The compiler will generate code to allow use of a `for in` loop on any type which conforms with `AsyncSequence`. The standard library will also extend the protocol to provide familiar generic algorithms. Here is an example which does not actually call an `async` function within its `next`, but shows the basic shape:--><p>编译器将会自动生成代码，让我们可以在符合 <code>AsyncSequence</code> 的任何类型上使用 <code>for in</code> 循环。标准库还将扩展协议以提供熟悉的通用算法。下面是一个例子，它实际上并没有在其 <code>next</code> 中调用 <code>async</code> 函数，但是展示了基本的概念：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Counter</span> : <span class="title class_ inherited__">AsyncSequence</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> howHigh: <span class="type">Int</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">AsyncIterator</span> : <span class="title class_ inherited__">AsyncIteratorProtocol</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> howHigh: <span class="type">Int</span></span><br><span class="line">    <span class="keyword">var</span> current <span class="operator">=</span> <span class="number">1</span></span><br><span class="line">    <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">next</span>() <span class="keyword">async</span> -&gt; <span class="type">Int</span>? &#123;</span><br><span class="line">      <span class="comment">// We could use the `Task` API to check for cancellation here and return early.</span></span><br><span class="line">      <span class="keyword">guard</span> current <span class="operator">&lt;=</span> howHigh <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">let</span> result <span class="operator">=</span> current</span><br><span class="line">      current <span class="operator">+=</span> <span class="number">1</span></span><br><span class="line">      <span class="keyword">return</span> result</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">makeAsyncIterator</span>() -&gt; <span class="type">AsyncIterator</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="type">AsyncIterator</span>(howHigh: howHigh)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--At the call site, using `Counter` would look like this:--><p>在调用方会这么使用 <code>counter</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> <span class="keyword">await</span> i <span class="keyword">in</span> <span class="type">Counter</span>(howHigh: <span class="number">3</span>) &#123;</span><br><span class="line">  <span class="built_in">print</span>(i)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* </span></span><br><span class="line"><span class="comment">Prints the following, and finishes the loop:</span></span><br><span class="line"><span class="comment">1</span></span><br><span class="line"><span class="comment">2</span></span><br><span class="line"><span class="comment">3</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> <span class="keyword">await</span> i <span class="keyword">in</span> <span class="type">Counter</span>(howHigh: <span class="number">3</span>) &#123;</span><br><span class="line">  <span class="built_in">print</span>(i)</span><br><span class="line">  <span class="keyword">if</span> i <span class="operator">==</span> <span class="number">2</span> &#123; <span class="keyword">break</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">Prints the following:</span></span><br><span class="line"><span class="comment">1</span></span><br><span class="line"><span class="comment">2</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><h2 id="具体设计"><a href="#具体设计" class="headerlink" title="具体设计"></a>具体设计</h2><!--Returning to our earlier example:--><p>回到我们之前的例子：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> <span class="keyword">try</span> <span class="keyword">await</span> line <span class="keyword">in</span> myFile.lines() &#123;</span><br><span class="line">  <span class="comment">// Do something with each line</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The compiler will emit the equivalent of the following code:--><p>编译器将生成类似于下面的代码：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> it <span class="operator">=</span> myFile.lines().makeAsyncIterator()</span><br><span class="line"><span class="keyword">while</span> <span class="keyword">let</span> line <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> it.next() &#123;</span><br><span class="line">  <span class="comment">// Do something with each line</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--All of the usual rules about error handling apply. For example, this iteration must be surrounded by `do/catch`, or be inside a `throws` function to handle the error. All of the usual rules about `await` also apply. For example, this iteration must be inside a context in which calling `await` is allowed like an `async` function.--><p>所有关于错误处理的常规规则都适用。例如，这个迭代必须被 <code>do/catch</code> 包围，或者在 <code>throws</code> 函数中处理错误。所有关于 <code>await</code> 的常规规则也适用。例如，这个迭代必须在一个允许调用 <code>await</code> 的上下文中，就像一个 <code>async</code> 函数一样。</p><h3 id="Cancellation"><a href="#Cancellation" class="headerlink" title="Cancellation"></a>Cancellation</h3><!--`AsyncIteratorProtocol` types should use the cancellation primitives provided by Swift's `Task` API, part of [structured concurrency](https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md). As described there, the iterator can choose how it responds to cancellation. The most common behaviors will be either throwing `CancellationError` or returning `nil` from the iterator. --><p><code>AsyncIteratorProtocol</code>类型应该使用 <a href="https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md">structured concurrency</a> 的一部分，Swift 的 <code>Task</code> API 提供的”取消”功能。正如那里面所描述的，迭代器可以选择如何响应“取消”。最常见的行为是抛出 <code>CancellationError</code> 或者让迭代器返回 <code>nil</code>。</p><!--If an `AsyncIteratorProtocol` type has cleanup to do upon cancellation, it can do it in two places:--><p>如果一个 <code>AsyncIteratorProtocol</code> 类型在取消时要清理资源，它可以在这两个地方进行：</p><!--1. After checking for cancellation using the `Task` API.2. In its `deinit` (if it is a class type).--><ol><li>使用 <code>Task</code> API 检查是否取消之后。</li><li>在其 <code>deinit</code> 中（如果它是 class 类型）。</li></ol><h3 id="Rethrows"><a href="#Rethrows" class="headerlink" title="Rethrows"></a>Rethrows</h3><!--This proposal will take advantage of a separate proposal to add specialized `rethrows` conformance in a protocol, pitched [here](https://forums.swift.org/t/pitch-rethrowing-protocol-conformances/42373). With the changes proposed there for `rethrows`, it will not be required to use `try` when iterating an `AsyncSequence` which does not itself throw.--><p>该提案将利用另一项提案，在协议中增加专门的 <code>rethrows</code> 一致性，该提案已在<a href="https://forums.swift.org/t/pitch-rethrowing-protocol-conformances/42373">这里</a>提出。根据该提案对 <code>rethrows</code> 的修改，当迭代一个本身不抛出的 <code>AsyncSequence</code> 时，将不需要使用 <code>try</code>。</p><!--The `await` is always required because the definition of the protocol is that it is always asynchronous.--><p>而 <code>await</code> 总是需要有的，因为协议的定义里它总是异步的。</p><h3 id="结束迭代"><a href="#结束迭代" class="headerlink" title="结束迭代"></a>结束迭代</h3><!--After an `AsyncIteratorProtocol` types returns `nil` or throws an error from its `next()` method, all future calls to `next()` must return `nil`. This matches the behavior of `IteratorProtocol` types and is important, since calling an iterator's `next()` method is the only way to determine whether iteration has finished.--> <p>在 <code>AsyncIteratorProtocol</code> 类型的 <code>next()</code> 方法返回 <code>nil</code> 或抛出错误之后，后续所有对 <code>next()</code> 调用都必须返回 <code>nil</code>。 与 <code>IteratorProtocol</code> 类型的行为保持一致，这很重要，因为调用迭代器的 <code>next()</code> 方法是确定迭代是否完成的唯一方法。</p><h2 id="AsyncSequence-函数"><a href="#AsyncSequence-函数" class="headerlink" title="AsyncSequence 函数"></a>AsyncSequence 函数</h2><!--The existence of a standard `AsyncSequence` protocol allows us to write generic algorithms for any type that conforms to it. There are two categories of functions: those that return a single value (and are thus marked as `async`), and those that return a new `AsyncSequence` (and are not marked as `async` themselves).--><p>标准的 <code>AsyncSequence</code> 协议的存在使我们能够为任何符合该协议的类型编写通用算法。有两类函数：返回一个单一值的函数（因此被标记为 <code>async</code>），和返回一个新的 <code>AsyncSequence</code> 的函数（本身没有标记为 <code>async</code>）。</p><!--The functions that return a single value are especially interesting because they increase usability by changing a loop into a single `await` line. Functions in this category are `first`, `contains`, `min`, `max`, `reduce`, and more. Functions that return a new `AsyncSequence` include `filter`, `map`, and `compactMap`.--><p>返回单个值的函数特别有趣，因为它们可以将一个循环改为一行 <code>await</code>，增加了可用性。例如 <code>first</code>、<code>contains</code>、<code>min</code>、<code>max</code>、<code>reduce</code> 等。返回一个新的 <code>AsyncSequence</code> 的函数有 <code>filter</code>、<code>map</code>、<code>compactMap</code>。</p><h3 id="将-AsyncSequence-转换为一个值"><a href="#将-AsyncSequence-转换为一个值" class="headerlink" title="将 AsyncSequence 转换为一个值"></a>将 AsyncSequence 转换为一个值</h3><!--Algorithms that reduce a for loop into a single call can improve readability of code. They remove the boilerplate required to set up and iterate a loop.--><p>将 for 循环缩减为一次调用的算法可以提高代码的可读性。它们消除了设置和迭代循环所需的模板。</p><!--For example, here is the `contains` function:--><p>例如，下面是 <code>contains</code> 函数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">AsyncSequence</span> <span class="keyword">where</span> <span class="type">Element</span> : <span class="type">Equatable</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">contains</span>(<span class="keyword">_</span> <span class="params">value</span>: <span class="type">Element</span>) <span class="keyword">async</span> <span class="keyword">rethrows</span> -&gt; <span class="type">Bool</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--With this extension, our "first long line" example from earlier becomes simply:--><p>通过这个扩展，我们前面的”超过 80 个字符的第一行”的例子就可以简化成这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> first <span class="operator">=</span> <span class="keyword">try?</span> <span class="keyword">await</span> myFile.lines().first(where: &#123; <span class="variable">$0</span>.count <span class="operator">&gt;</span> <span class="number">80</span> &#125;)</span><br></pre></td></tr></table></figure><!--Or, if the sequence should be processed asynchonously and used later:--><p>或者，如果该序列应该被异步处理并且在之后才会使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">let</span> first <span class="operator">=</span> myFile.lines().first(where: &#123; <span class="variable">$0</span>.count <span class="operator">&gt;</span> <span class="number">80</span> &#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// later</span></span><br><span class="line"></span><br><span class="line">warnAboutLongLine(<span class="keyword">try?</span> <span class="keyword">await</span> first)</span><br></pre></td></tr></table></figure><!--The following functions will be added to `AsyncSequence`:--><p><code>AsyncSequence</code> 将增加以下功能：</p><table><thead><tr><th>函数</th><th>备注</th></tr></thead><tbody><tr><td><code>contains(_ value: Element) async rethrows -&gt; Bool</code></td><td><code>Element</code> 需要遵循 <code>Equatable</code></td></tr><tr><td><code>contains(where: (Element) async throws -&gt; Bool) async rethrows -&gt; Bool</code></td><td>闭包的 <code>async</code> 是可选的</td></tr><tr><td><code>allSatisfy(_ predicate: (Element) async throws -&gt; Bool) async rethrows -&gt; Bool</code></td><td></td></tr><tr><td><code>first(where: (Element) async throws -&gt; Bool) async rethrows -&gt; Element?</code></td><td></td></tr><tr><td><code>min() async rethrows -&gt; Element?</code></td><td><code>Element</code> 需要遵循 <code>Comparable</code></td></tr><tr><td><code>min(by: (Element, Element) async throws -&gt; Bool) async rethrows -&gt; Element?</code></td><td></td></tr><tr><td><code>max() async rethrows -&gt; Element?</code></td><td><code>Element</code> 需要遵循 <code>Comparable</code></td></tr><tr><td><code>max(by: (Element, Element) async throws -&gt; Bool) async rethrows -&gt; Element?</code></td><td></td></tr><tr><td><code>reduce&lt;T&gt;(_ initialResult: T, _ nextPartialResult: (T, Element) async throws -&gt; T) async rethrows -&gt; T</code></td><td></td></tr><tr><td><code>reduce&lt;T&gt;(into initialResult: T, _ updateAccumulatingResult: (inout T, Element) async throws -&gt; ()) async rethrows -&gt; T</code></td><td></td></tr></tbody></table><h3 id="将-AsyncSequence-转换为另一个-AsyncSequence"><a href="#将-AsyncSequence-转换为另一个-AsyncSequence" class="headerlink" title="将 AsyncSequence 转换为另一个 AsyncSequence"></a>将 AsyncSequence 转换为另一个 AsyncSequence</h3><!--These functions on `AsyncSequence` return a result which is itself an `AsyncSequence`. Due to the asynchronous nature of `AsyncSequence`, the behavior is similar in many ways to the existing `Lazy` types in the standard library. Calling these functions does not eagerly `await` the next value in the sequence, leaving it up to the caller to decide when to start that work by simply starting iteration when they are ready.--><p>这些关于 <code>AsyncSequence</code> 的函数会返回一个结果，这个结果本身就是一个 <code>AsyncSequence</code>。由于 <code>AsyncSequence</code> 的异步性质，其行为在许多方面与标准库中现有的 <code>Lazy</code> 类型相似。调用这些函数并不急于 <code>await</code> 序列中的下一个值，而是由调用者决定何时开始该工作，只需在准备好时开始迭代即可。</p><!--As an example, let's look at `map`:--><p>举个例子，让我们看看 <code>map</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">AsyncSequence</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">map</span>&lt;<span class="type">Transformed</span>&gt;(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">transform</span>: <span class="keyword">@escaping</span> (<span class="type">Element</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Transformed</span></span><br><span class="line">  ) -&gt; <span class="type">AsyncMapSequence</span>&lt;<span class="keyword">Self</span>, <span class="type">Transformed</span>&gt;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">AsyncMapSequence</span>&lt;<span class="type">Upstream</span>: <span class="type">AsyncSequence</span>, <span class="type">Transformed</span>&gt;: <span class="title class_ inherited__">AsyncSequence</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">let</span> upstream: <span class="type">Upstream</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">let</span> transform: (<span class="type">Upstream</span>.<span class="type">Element</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Transformed</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Iterator</span> : <span class="title class_ inherited__">AsyncIterator</span> &#123; </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">next</span>() <span class="keyword">async</span> <span class="keyword">rethrows</span> -&gt; <span class="type">Transformed</span>?</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--For each of these functions, we first define a type which conforms with the `AsyncSequence` protocol. The name is modeled after existing standard library `Sequence` types like `LazyDropWhileCollection` and `LazyMapSequence`. Then, we add a function in an extension on `AsyncSequence` which creates the new type (using `self` as the `upstream`) and returns it.--><p>对于这种函数，我们首先定义一个符合 <code>AsyncSequence</code> 协议的类型，这个名字是仿照现有的标准库 <code>Sequence</code> 类型，如 <code>LazyDropWhileCollection</code> 和 <code>LazyMapSequence</code>。这个名字是仿照现有的标准库 <code>Sequence</code> 类型，如 <code>LazyDropWhileCollection</code> 和 <code>LazyMapSequence</code>。然后，我们在 <code>AsyncSequence</code> 上的扩展中添加一个函数，该函数创建新的类型（使用 <code>self</code> 作为 <code>upstream</code>）并返回。</p><table><thead><tr><th>函数</th></tr></thead><tbody><tr><td><code>map&lt;T&gt;(_ transform: (Element) async throws -&gt; T) -&gt; AsyncMapSequence</code></td></tr><tr><td><code>compactMap&lt;T&gt;(_ transform: (Element) async throws -&gt; T?) -&gt; AsyncCompactMapSequence</code></td></tr><tr><td><code>flatMap&lt;SegmentOfResult: AsyncSequence&gt;(_ transform: (Element) async throws -&gt; SegmentOfResult) async rethrows -&gt; AsyncFlatMapSequence</code></td></tr><tr><td><code>drop(while: (Element) async throws -&gt; Bool) async rethrows -&gt; AsyncDropWhileSequence</code></td></tr><tr><td><code>dropFirst(_ n: Int) async rethrows -&gt; AsyncDropFirstSequence</code></td></tr><tr><td><code>prefix(while: (Element) async throws -&gt; Bool) async rethrows -&gt; AsyncPrefixWhileSequence</code></td></tr><tr><td><code>prefix(_ n: Int) async rethrows -&gt; AsyncPrefixSequence</code></td></tr><tr><td><code>filter(_ predicate: (Element) async throws -&gt; Bool) async rethrows -&gt; AsyncFilterSequence</code></td></tr></tbody></table><h2 id="未来的改进方向"><a href="#未来的改进方向" class="headerlink" title="未来的改进方向"></a>未来的改进方向</h2><!--The following topics are things we consider important and worth discussion in future proposals:--><p>以下是我们认为重要且值得在今后的提案中讨论的话题：</p><h3 id="补充更多-AsyncSequence-函数"><a href="#补充更多-AsyncSequence-函数" class="headerlink" title="补充更多 AsyncSequence 函数"></a>补充更多 <code>AsyncSequence</code> 函数</h3><!--We've aimed for parity with the most relevant `Sequence` functions. There may be others that are worth adding in a future proposal.--><p>这个提案的目标是与最相关的 <code>Sequence</code> 函数保持一致。可能还有其他值得在今后的提案中添加的功能。</p><!--API which uses a time argument must be coordinated with the discussion about `Executor` as part of the [structured concurrency proposal](https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md).--><p>使用时间作为参数的 API 必须与<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md">结构化并发提案</a>中的 <code>Executor</code> 一起进行讨论。</p><!--We would like a `first` property, but properties cannot currently be `async` or `throws`. Discussions are ongoing about adding a capability to the language to allow effects on properties. If those features become part of Swift then we should add a `first` property to `AsyncSequence`.--><p>我们希望有一个 <code>first</code> 属性，但目前 Swift 里的属性不能标记为 <code>async</code> 或 <code>throws</code>。目前正在讨论在语言中增加一种能力，以允许对属性产生影响。如果这些功能成为了 Swift 的一部分，那么我们应该在 <code>AsyncSequence</code> 中添加一个 <code>first</code> 属性。</p><h3 id="AsyncSequence-Builder"><a href="#AsyncSequence-Builder" class="headerlink" title="AsyncSequence Builder"></a>AsyncSequence Builder</h3><!--In the standard library we have not only the `Sequence` and `Collection` protocols, but concrete types which adopt them (for example, `Array`). We will need a similar API for `AsyncSequence` that makes it easy to construct a concrete instance when needed, without declaring a new type and adding protocol conformance.--><p>在标准库中，我们不仅有 <code>Sequence</code> 和 <code>Collection</code> 协议，还有采用这些协议的具体类型（例如 <code>Array</code>）。我们需要为 <code>AsyncSequence</code> 提供一个类似的 API，以便在需要的时候很容易地构造一个具体的实例，而不需要声明一个新的类型和增加协议的一致性。</p><h2 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a>代码兼容性</h2><!--This new functionality will be source compatible with existing Swift.--><p>这个新功能将与现有的 Swift 代码兼容。</p><h2 id="对于-ABI-稳定性的影响"><a href="#对于-ABI-稳定性的影响" class="headerlink" title="对于 ABI 稳定性的影响"></a>对于 ABI 稳定性的影响</h2><!--This change is additive to the ABI.--><p>这里的修改是对 ABI 的补充性修改。</p><h2 id="对于-API-兼容性的影响"><a href="#对于-API-兼容性的影响" class="headerlink" title="对于 API 兼容性的影响"></a>对于 API 兼容性的影响</h2><!--This change is additive to API.--><p>这里的修改是对 API 的补充性修改。</p><h2 id="其它方案"><a href="#其它方案" class="headerlink" title="其它方案"></a>其它方案</h2><h3 id="显式-Cancellation"><a href="#显式-Cancellation" class="headerlink" title="显式 Cancellation"></a>显式 Cancellation</h3><!--An earlier version of this proposal included an explicit `cancel` function. We removed it for the following reasons:--><p>该提案的早期版本包括一个明确的 <code>cancel</code> 功能。出于以下原因，我们决定将它删除掉：</p><!--1. Reducing the requirements of implementing `AsyncIteratorProtocol` makes it simpler to use and easier to understand. The rules about when `cancel` would be called, while straightforward, would nevertheless be one additional thing for Swift developers to learn.2. The structured concurrency proposal already includes a definition of cancellation that works well for `AsyncSequence`. We should consider the overall behavior of cancellation for asynchronous code as one concept.--><ol><li>减少实现 <code>AsyncIteratorProtocol</code> 的要求，使其更容易使用和理解。关于什么时候调用 <code>cancel</code> 的规则，虽然简单明了，但对于 Swift 开发者来说，还是要多学一样东西。</li><li>结构化并发提案中已经包含了一个对 <code>AsyncSequence</code> 取消的明确定义。我们应该把异步代码的取消行为整体视为一个概念。</li></ol><h3 id="异步-Cancellation"><a href="#异步-Cancellation" class="headerlink" title="异步 Cancellation"></a>异步 Cancellation</h3><!--If we used explicit cancellation, the `cancel()` function on the iterator could be marked as `async`. However, this means that the implicit cancellation done when leaving a `for/in` loop would require an implicit `await` -- something we think is probably too much to hide from the developer. Most cancellation behavior is going to be as simple as setting a flag to check later, so we leave it as a synchronous function and encourage adopters to make cancellation fast and non-blocking.--><p>如果我们使用显式取消，迭代器的 <code>cancel()</code> 函数可以标记为 <code>async</code>。然而，这意味着在离开 <code>for/in</code> 循环时进行的隐式取消将需要一个隐式的 <code>await</code> – 我们认为这对开发者来说可能太过隐蔽。大多数的取消行为会像设置一个标志以便稍后检查一样简单，所以我们让它作为一个同步函数存在，鼓励采用者让取消行为可以快速执行并且不会阻塞。</p><h3 id="Opaque-类型"><a href="#Opaque-类型" class="headerlink" title="Opaque 类型"></a>Opaque 类型</h3><!--Each `AsyncSequence`-to-`AsyncSequence` algorithm will define its own concrete type. We could attempt to hide these details behind a general purpose type eraser. We believe leaving the types exposed gives us (and the compiler) more optimization opportunities. A great future enhancement would be for the language to support `some AsyncSequence where Element=...`-style syntax, allowing hiding of concrete `AsyncSequence` types at API boundaries.--><p>每个 <code>AsyncSequence</code> 到 <code>AsyncSequence</code> 的算法都会定义自己的具体类型。我们可以尝试将这些细节隐藏在一个通用的类型擦除器后面。我们相信让类型暴露出来会给我们（和编译器）更多的优化机会。未来的一个潜在的巨大改进是让语言支持 <code>some AsyncSequence where Element=...</code> 风格的语法，允许在 API 里隐藏具体的 <code>AsyncSequence</code> 类型。</p><h3 id="复用-Sequence"><a href="#复用-Sequence" class="headerlink" title="复用 Sequence"></a>复用 Sequence</h3><!--If the language supported a `reasync` concept, then it seems plausible that the `AsyncSequence` and `Sequence` APIs could be merged. However, we believe it is still valuable to consider these as two different types. The added complexity of a time dimension in asynchronous code means that some functions need more configuration options or more complex implementations. Some algorithms that are useful on asynchronous sequences are not meaningful on synchronous ones. We prefer not to complicate the API surface of the synchronous collection types in these cases.--><p>如果语言支持 <code>reasync</code> 概念，那么 <code>AsyncSequence</code> 和 <code>Sequence</code> API 合并起来似乎是合理的。然而，我们认为，将其视为两种不同的类型仍然是有价值的。异步代码中增加了时间维度的复杂性，这意味着一些函数需要更多的配置选项或更复杂的实现。一些在异步序列上有用的算法在同步序列上是没有意义的。在这些情况下，我们最好不要让同步集合类型的 API 复杂化。</p><h3 id="命名"><a href="#命名" class="headerlink" title="命名"></a>命名</h3><!--The names of the concrete `AsyncSequence` types is designed to mirror existing standard library API like `LazyMapSequence`. Another option is to introduce a new pattern with an empty enum or other namespacing mechanism.--><p>具体的 <code>AsyncSequence</code> 类型的名称被设计为镜像现有的标准库 API，如 <code>LazyMapSequence</code>。另一种选择是用一个空的枚举或其他命名机制引入一个新的模式。</p><!--We considered `AsyncGenerator` but would prefer to leave the `Generator` name for future language enhancements. `Stream` is a type in Foundation, so we did not reuse it here to avoid confusion.--><p>我们考虑过 <code>AsyncGenerator</code>，但希望保留 <code>Generator</code> 这个名称给后续的功能实用。<code>Stream</code> 是Foundation 中的一个类型，因此我们没有在这里重复使用，避免混淆。</p><h3 id="await-in"><a href="#await-in" class="headerlink" title="await in"></a><code>await in</code></h3><!--We considered a shorter syntax of `await...in`. However, since the behavior here is fundamentally a loop, we feel it is important to use the existing `for` keyword as a strong signal of intent to readers of the code. Although there are a lot of keywords, each one has purpose and meaning to readers of the code.--><p>我们考虑过一个更短的 <code>await...in</code> 的语法。然而，由于这里的行为从根本上说是一个循环，我们认为必须使用现有的 <code>for</code> 关键字作为对代码读者的强烈信号。虽然有很多关键字，但它们每一个关键字对代码的读者来说都有目的和意义。</p><h3 id="添加-API-到-Iterator-里（而不是-Sequence）"><a href="#添加-API-到-Iterator-里（而不是-Sequence）" class="headerlink" title="添加 API 到 Iterator 里（而不是 Sequence）"></a>添加 API 到 Iterator 里（而不是 Sequence）</h3><!--We discussed applying the fundamental API (`map`, `reduce`, etc.) to `AsyncIteratorProtocol` instead of `AsyncSequence`. There has been a long-standing (albeit deliberate) ambiguity in the `Sequence` API -- is it supposed to be single-pass or multi-pass? This new kind of iterator & sequence could provide an opportunity to define this more concretely.--><p>我们讨论了将基本 API(<code>map</code>、<code>reduce</code> 等)应用于 <code>AsyncIteratorProtocol</code> 而不是 <code>AsyncSequence</code>。在 <code>Sequence</code> API 中一直存在着一个长期的（虽然是故意的）歧义 – 它到底应该是一次还是多次循环？这个新的迭代器和序列提供了一个更具体地定义这个问题的机会。</p><!--While it is tempting to use this new API to right past wrongs, we maintain that the high level goal of consistency with existing Swift concepts is more important. --><p>虽然很想用这个新的API来纠正过去的错误，但我们认为，与现有 Swift 概念保持一致的原则性目标更为重要。</p><!--For example, `for...in` cannot be used on an `IteratorProtocol` -- only a `Sequence`. If we chose to make `AsyncIteratorProtocol` use `for...in` as described here, that leaves us with the choice of either introducing an inconsistency between `AsyncIteratorProtocol` and `IteratorProtocol` or giving up on the familiar `for...in` syntax. Even if we decided to add `for...in` to `IteratorProtocol`, it would still be inconsistent because we would be required to leave `for...in` syntax on the existing `Sequence`.--><p>例如，<code>for...in</code> 不能用于 <code>IteratorProtocol</code> – 只能用于 <code>Sequence</code>。如果我们选择让 <code>AsyncIteratorProtocol</code> 使用这里描述的 <code>for...in</code>，那我们就只能选择在 <code>AsyncIteratorProtocol</code> 和 <code>IteratorProtocol</code> 之间引入不一致的行为，或者放弃熟悉的 <code>for...in</code> 语法。即使我们决定在 <code>IteratorProtocol</code> 中加入 <code>for...in</code>，它仍然是不一致的，因为我们需要在现有的 <code>Sequence</code> 上留下 <code>for...in</code> 语法。</p><!--Another point in favor of consistency is that implementing an `AsyncSequence` should feel familiar to anyone who knows how to implement a `Sequence`.--><p>倾向于保持一致性的另一个原因是，实现 <code>AsyncSequence</code> 对于任何知道如何实现 <code>Sequence</code> 的人来说都会感到熟悉。</p><!--We are hoping for widespread adoption of the protocol in API which would normally have instead used a `Notification`, informational delegate pattern, or multi-callback closure argument. In many of these cases we feel like the API should return the 'factory type' (an `AsyncSequence`) so that it can be iterated again. It will still be up to the caller to be aware of any underlying cost of performing that operation, as with iteration of any `Sequence` today.--><p>我们希望在 API 中广泛采用该协议，通常情况下，API 会使用 <code>Notification</code>、delegate 或者回调。在大部分这样的情况下，我们觉得 API 应该返回 “工厂类型”（一个 <code>AsyncSequence</code>），以便它可以再次迭代。调用者仍然需要知道执行该操作的任何基本成本，就像今天任何 <code>Sequence</code> 的迭代一样。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2021-03-10/</id>
    <link href="https://kemchenj.github.io/2021-03-10/"/>
    <published>2021-03-09T16:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md">SE-0298 Async&#x2F;Await: Sequences</a></p>
</blockquote>
<ul>
<li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md">SE-0298</a></li>
<li>Authors: <a href="https://github.com/parkera">Tony Parker</a>, <a href="https://github.com/phausler">Philippe Hausler</a></li>
<li>Review Manager: <a href="https://github.com/DougGregor">Doug Gregor</a></li>
<li>Status: <strong>Implemented (Swift 5.5)</strong></li>
<li>Implementation: <a href="https://github.com/apple/swift/pull/35224">apple&#x2F;swift#35224</a></li>
<li>Decision Notes: <a href="https://forums.swift.org/t/accepted-with-modification-se-0298-async-await-sequences/44231">Rationale</a></li>
<li>Revision: Based on <a href="https://forums.swift.org/t/pitch-clarify-end-of-iteration-behavior-for-asyncsequence/45548">forum discussion</a></li>
</ul>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--
Swift's [async/await](https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md) feature provides an intuitive, built-in way to write and use functions that return a single value at some future point in time. We propose building on top of this feature to create an intuitive, built-in way to write and use functions that return many values over time.
-->

<p>Swift 的 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md">async&#x2F;await</a> 特性提供了一种直观的、内建的方式来编写和使用在未来某个时间点返回一个值的函数。我们建议在这个特性的基础上，添加一种直观的、内置的方式来编写和使用在一段时间内返回多个值的函数。</p>
<!--
This proposal is composed of the following pieces:
-->

<p>本提案由以下三个部分组成：</p>
<!--
1. A standard library definition of a protocol that represents an asynchronous sequence of values
2. Compiler support to use `for...in` syntax on an asynchronous sequence of values
3. A standard library implementation of commonly needed functions that operate on an asynchronous sequence of values
-->

<ol>
<li>增加一个表示异步序列的协议到标准库里</li>
<li>在异步序列上使用 <code>for...in</code> 语法的编译器支持</li>
<li>对异步序列进行操作的通用函数的标准库实现</li>
</ol>]]>
    </summary>
    <title>【译】SE-0298 Async/Await 序列</title>
    <updated>2026-09-02T11:08:19.193Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<blockquote><p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md">SE-0297 Concurrency Interoperability with Objective-C</a></p></blockquote><ul><li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md">SE-0297</a></li><li>Author: <a href="https://github.com/DougGregor">Doug Gregor</a></li><li>Review Manager: <a href="https://github.com/lattner">Chris Lattner</a></li><li>Status: <strong>Implemented (Swift 5.5)</strong></li><li><a href="https://forums.swift.org/t/accepted-with-modifications-se-0297-concurrency-interoperability-with-objective-c/43306">Acceptance Post</a></li><li>Implementation: Partially available in <a href="https://swift.org/download/#snapshots">recent <code>main</code> snapshots</a> behind the flag <code>-Xfrontend -enable-experimental-concurrency</code></li></ul><!--## Table of Contents* [Introduction](#introduction)* [Motivation](#motivation)* [Proposed solution](#proposed-solution)* [Detailed design](#detailed-design)   * [Asynchronous completion-handler methods](#asynchronous-completion-handler-methods)   * [Defining asynchronous @objc methods in Swift](#defining-asynchronous-objc-methods-in-swift)   * [Actor classes](#actor-classes)   * [Completion handlers must be called exactly once](#completion-handlers-must-be-called-exactly-once)   * [Additional Objective-C attributes](#additional-objective-c-attributes)* [Source compatibility](#source-compatibility)* [Revision history](#revision-history)* [Future Directions](#future-directions)   * [NSProgress](#nsprogress)--><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--Swift's concurrency feature involves asynchronous functions and actors. While Objective-C does not have corresponding language features, asynchronous APIs are common in Objective-C, expressed manually through the use of completion handlers. This proposal provides bridging between Swift's concurrency features (e.g., `async` functions) and the convention-based expression of asynchronous functions in Objective-C. It is intended to allow the wealth of existing asynchronous Objective-C APIs to be immediately usable with Swift's concurrency model.--><p>Swift 的并发功能包括了异步函数和 actor。虽然 Objective-C 没有相应的语言特性，但异步 API 在 Objective-C 中很常见，通过使用 completion-handler 手动实现。本提案提供了 Swift 的并发特性（如 <code>async</code> 函数）和 Objective-C 中基于约定的异步函数表达之间的桥接。它的目的是让现有的丰富的异步 Objective-C API 可以立即与 Swift 的并发模型一起使用。</p><span id="more"></span><!--For example, consider the following Objective-C API in [CloudKit](https://developer.apple.com/documentation/cloudkit/ckcontainer/1640387-fetchshareparticipantwithuserrec):--><p>例如，试想一下 <a href="https://developer.apple.com/documentation/cloudkit/ckcontainer/1640387-fetchshareparticipantwithuserrec">CloudKit</a> 中的 Objective-C API：</p><figure class="highlight objectivec"><table><tr><td class="code"><pre><span class="line">- (<span class="type">void</span>)fetchShareParticipantWithUserRecordID:(CKRecordID *)userRecordID </span><br><span class="line">    completionHandler:(<span class="type">void</span> (^)(CKShareParticipant * _Nullable, <span class="built_in">NSError</span> * _Nullable))completionHandler;</span><br></pre></td></tr></table></figure><!--This API is asynchronous. It delivers its result (or an error) via completion handler. The API directly translates into Swift:--><p>这个 API 是异步的，它通过 completion-handler 来提供它的结果（或错误），这个 API 直接翻译到 Swift：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">fetchShareParticipant</span>(</span><br><span class="line">    <span class="params">withUserRecordID</span> <span class="params">userRecordID</span>: <span class="type">CKRecord</span>.<span class="type">ID</span>, </span><br><span class="line">    <span class="params">completionHandler</span>: <span class="keyword">@escaping</span> (<span class="type">CKShare</span>.<span class="type">Participant</span>?, <span class="type">Error</span>?) -&gt; <span class="type">Void</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><!--Existing Swift code can call this API by passing a closure for the completion handler. This proposal provides an alternate Swift translation of the API into an `async` function, e.g.,--><p>现有的 Swift 代码可以通过向 completion-handler 传入一个闭包来调用这个 API。这个提案提供了一个新的 API 翻译方案，可以将这一类 API 翻译为 <code>async</code> 函数，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">fetchShareParticipant</span>(</span><br><span class="line">    <span class="params">withUserRecordID</span> <span class="params">userRecordID</span>: <span class="type">CKRecord</span>.<span class="type">ID</span></span><br><span class="line">) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">CKShare</span>.<span class="type">Participant</span></span><br></pre></td></tr></table></figure><!--Swift callers can invoke `fetchShareParticipant(withUserRecordID:)` within an `await` expression:--><p>Swift 调用者可以使用 <code>await</code> 表达式来调用 <code>fetchShareParticipant(withUserRecordID:)</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">guard</span> <span class="keyword">let</span> participant <span class="operator">=</span> <span class="keyword">try?</span> <span class="keyword">await</span> container.fetchShareParticipant(withUserRecordID: user) <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Swift-evolution thread: <a href="https://forums.swift.org/t/concurrency-interoperability-with-objective-c/41616">[Concurrency] Interoperability with Objective-C</a></p><h2 id="动机"><a href="#动机" class="headerlink" title="动机"></a>动机</h2><!--On Apple platforms, Swift's tight integration with Objective-C APIs is an important part of the developer experience. There are several core features:--><p>在 Apple 的平台上，Swift 与 Objective-C API 的紧密集成是开发者体验的重要组成部分之一。有这几个核心功能：</p><!--* Objective-C classes, protocols, and methods can be used directly from Swift.* Swift classes can subclass Objective-C classes.* Swift classes can declare conformance to Objective-C protocols.* Swift classes, protocols, and methods can be made available to Objective-C via the `@objc` attribute.--><ul><li>Objective-C 类、协议和方法可以直接在 Swift 中使用。</li><li>Swift 类可以继承 Objective-C 类。</li><li>Swift 类可以声明与 Objective-C 协议的 conformance。</li><li>Swift 类、协议和方法可以通过 <code>@objc</code> 注解提供给 Objective-C。</li></ul><!--Asynchronous APIs abound in Objective-C code: the iOS 14.0 SDK includes nearly 1,000 methods that accept completion handlers. These include methods that one could call directly from Swift, methods that one would override in a Swift-defined subclass, and methods in protocols that one would conform to. Supporting these use cases in Swift's concurrency model greatly expands the reach of this new feature. --><p>异步 API 在 Objective-C 代码中比比皆是：iOS 14.0 SDK 中包含了近 1000 个接收 completion-handler 的方法，其中包括可以从 Swift 中直接调用的方法，可以在 Swift 定义的子类中 override 的方法，以及 conform 协议的方法。在 Swift 的并发模型中支持这些用例，可以大大扩展这个新功能的应用范围。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><!--The proposed solution provides interoperability between Swift's concurrency constructs and Objective-C in various places. It has several inter-dependent pieces:--><p>本提案提出的解决方案，尝试在几个不同的维度提供 Swift 并发结构和 Objective-C 之间的交互。它包含了这几个相互依赖的组成部分：</p><!--* Translate Objective-C completion-handler methods into `async` methods in Swift.* Allow `async` methods defined in Swift to be `@objc`, in which case they are exported as completion-handler methods.* Provide Objective-C attributes to control over how completion-handler-based APIs are translated into `async` Swift functions.--><ul><li>在 Swift 中把 Objective-C 接收 completion-handler 的函数翻译成 <code>async</code> 方法。</li><li>允许在 Swift 中定义的 <code>async</code> 方法被注解为 <code>@objc</code>，在这种情况下，它们将导出为基于 completion-handler 的方法。（供 Objective-C 调用）</li><li>提供 Objective-C 注解，基于 completion-handler 的 API 转化为 <code>async</code> Swift函数的流程可以用它来控制。</li></ul><!--The detailed design section describes the specific rules and heuristics being applied. However, the best way to evaluate the overall effectiveness of the translation is to see its effect over a large number of Objective-C APIs. [This pull request](https://github.com/DougGregor/swift-concurrency-objc/pull/1) demonstrates the effect that this proposal has on the Swift translations of Objective-C APIs across the Apple iOS, macOS, tvOS, and watchOS SDKs.--><p>下面的设计细节描述了具体的规则和推导方法。然而，评估整体翻译效果的最佳方式还是查看它应用在 Objective-C API 上的实际效果。<a href="https://github.com/DougGregor/swift-concurrency-objc/pull/1">这个 Pull Reqeust</a> 展示了这个提案对苹果 iOS、macOS、tvOS 和 watchOS SDK 中Objective-C API 的 Swift 翻译的效果。</p><h2 id="设计细节"><a href="#设计细节" class="headerlink" title="设计细节"></a>设计细节</h2><h3 id="异步-completion-handler-方法"><a href="#异步-completion-handler-方法" class="headerlink" title="异步 completion-handler 方法"></a>异步 completion-handler 方法</h3><!--An Objective-C method is potentially an asynchronous completion-handler method if it meets the following requirements:--><p>如果一个 Objective-C 方法满足以下要求，那它就可以看作是一个异步 completion-handler 方法：</p><!--* The method has a completion handler parameter, which is an Objective-C block that will receive the "result" of the asynchronous computation. It must meet the following additional constraints:  * It has a `void` result type.  * It is called exactly once along all execution paths through the implementation.  * If the method can deliver an error, one of the parameters of the block is of type `NSError *` that is not `_Nonnull`. A non-nil `NSError *` value typically indicates that an error occurred, although the C `swift_async` attribute can describe other conventions (discussed in the section on Objective-C attributes).* The method itself has a `void` result type, because all results are delivered by the completion handler block.--><ul><li>该方法有一个 completion-handler 参数，它是一个 Objective-C 闭包，接收异步运算的”结果”。它必须满足以下额外的限制条件： <ul><li>它的返回值类型是 <code>void</code>。</li><li>它在整个实现的所有执行路径中只被调用一次。</li><li>如果它可以传入 error，并且闭包包含了一个是类型为 <code>NSError *</code> 的参数，并且不是 <code>_Nonnull</code>。一个非 null 的 <code>NSError *</code> 值通常表示发生了 error，尽管 C 语言的 <code>swift_async</code> 属性可以使用其他约定（在 Objective-C 注解一节中讨论）。</li></ul></li><li>方法本身的返回值类型是 <code>void</code>，因为所有的结果都是由 completion-handler 闭包传递的。</li></ul><!--An Objective-C method that is potentially an asynchronous completion-handler method will be translated into an `async` method when it is either annotated explicitly with an appropriate `swift_async` attribute (described in the section on Objective-C attributes) or is implicitly inferred when the following heuristics successfully identify the completion handler parameter:--><p>一个可能是异步 completion-handler 方法的 Objective-C 方法将被翻译成一个 <code>async</code> 方法，当它被显式地注解为一个适当的 <code>swift_async</code> 属性（在 Objective-C 注解一节中有详细说明），或者当下面的推导成功地识别出 completion-handler 参数时，它就被隐式地推导为 async 方法：</p><!--* If the method has a single parameter, and the suffix of the first selector piece is one of the following phrases:  - `WithCompletion`  - `WithCompletionHandler`  - `WithCompletionBlock`  - `WithReplyTo`  - `WithReply`  the sole parameter is the completion handler parameter. The matching phrase will be removed from the base name of the function when it is imported.* If the method has more than one parameter, the last parameter is the completion handler parameter if its selector piece or parameter name is `completion`, `withCompletion`, `completionHandler`, `withCompletionHandler`, `completionBlock`, `withCompletionBlock`, `replyTo`, `withReplyTo`,  `reply`, or `replyTo`.* If the method has more than one parameter, and the last parameter ends with one of the suffixes from the first bullet, the last parameter is the completion handler. The text preceding the suffix is appended to the base name of the function.--><ul><li>如果该方法只有一个参数，而且第一个 selector 的后缀是下列短语之一：<ul><li><code>WithCompletion</code></li><li><code>WithCompletionHandler</code></li><li><code>WithCompletionBlock</code></li><li><code>WithReplyTo</code></li><li><code>WithReply</code><br>唯一参数是 completion-handler 参数。当导入函数时，匹配的短语将从函数的名字中移除。</li></ul></li><li>如果方法有一个以上的参数，如果它的 selector 外参或内参名字是 <code>completion</code>、<code>withCompletion</code>、 <code>completionHandler</code>、 <code>withCompletionHandler</code>、 <code>completionBlock</code>、 <code>withCompletionBlock</code>、 <code>replyTo</code>、 <code>withReplyTo</code>、 <code>reply</code> 或 <code>replyTo</code>，则最后一个参数看作是 completion-handler 参数。</li><li>如果方法有一个以上的参数，并且最后一个参数以第一个规则中的其中一个后缀结尾，则最后一个参数将被推导为 completion-handler。后缀前面的文字被附加到函数的名称里。</li></ul><!--When the completion handler parameter is inferred, the presence of an `NSError *` parameter that is not `_Nonnull` in the completion handler block type indicates that the translated method can deliver an error.--><p>当推导出 completion-handler 参数时，如果 completion-handler 闭包类型中存在一个不是 <code>_Nonnull</code> 的 <code>NSError *</code> 参数，则表明翻译后的方法可以传递 error。</p><!--The translation of an asynchronous Objective-C completion-handler method into an `async` Swift method follows the normal translation procedure, with the following alterations:--><p>将一个异步的 Objective-C completion-handler 方法翻译成一个 <code>async</code>  Swift 方法将遵循正常的翻译规则，但做了以下改动：</p><!--* The completion handler parameter is removed from the parameter list of the translated Swift method.* If the method can deliver an error, it is `throws` in addition to being `async`.* The parameter types of the completion handler block type are translated into the result type of the `async` method, subject to the following additional rules:  * If the method can deliver an error, the `NSError *` parameter is ignored.   * If the method can deliver an error and a given parameter has the `_Nullable_result` nullability qualifier (see the section on Objective-C attributes below), it will be imported as optional. Otherwise, it will be imported as non-optional.  * If there are multiple parameter types, they will be combined into a tuple type.--><ul><li>completion-handler 参数从翻译后的 Swift 方法的参数列表中删除。</li><li>如果该方法可以传入一个 error，那么它除了是 <code>async</code> 之外，还会是 <code>throws</code> 的。</li><li>completion-handler 闭包的参数类型会被翻译成 <code>async</code> 方法的结果类型，但要遵守以下附加规则：<ul><li>如果该方法可以传入一个 error，则忽略 <code>NSError *</code> 参数。</li><li>如果该方法可以传入一个 error，并且给定的参数具有 <code>_Nullable_result</code> nullability 的标注（参见下面的 Objective-C 注解一节），那么它将作为 optional 参数被导入。否则，它将被作为 non-optional 参数导入。</li><li>如果有多个参数类型，它们将被合并成一个元组类型。</li></ul></li></ul><!--The following [PassKit API](https://developer.apple.com/documentation/passkit/pkpasslibrary/3543357-signdata?language=objc) demonstrates how the inference rule plays out:--><p>下面 <a href="https://developer.apple.com/documentation/passkit/pkpasslibrary/3543357-signdata?language=objc">PassKit 的 API</a> 展示了推导规则是怎么发挥作用的：</p><figure class="highlight objectivec"><table><tr><td class="code"><pre><span class="line">- (<span class="type">void</span>)signData:(<span class="built_in">NSData</span> *)signData </span><br><span class="line">withSecureElementPass:(PKSecureElementPass *)secureElementPass </span><br><span class="line">      completion:(<span class="type">void</span> (^)(<span class="built_in">NSData</span> *signedData, <span class="built_in">NSData</span> *signature, <span class="built_in">NSError</span> *error))completion;</span><br></pre></td></tr></table></figure><!--Today, this is translated into the following completion-handler function in Swift:--><p>目前这个函数在 Swift 中被翻译成这样的 completion-handler 函数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">sign</span>(<span class="keyword">_</span> <span class="params">signData</span>: <span class="type">Data</span>, </span><br><span class="line">    <span class="params">using</span> <span class="params">secureElementPass</span>: <span class="type">PKSecureElementPass</span>, </span><br><span class="line">    <span class="params">completion</span>: <span class="keyword">@escaping</span> (<span class="type">Data</span>?, <span class="type">Data</span>?, <span class="type">Error</span>?) -&gt; <span class="type">Void</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><!--This will be translated into the following `async` function:--><p>它将会被翻译成这样的 <code>async</code> 函数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">sign</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">signData</span>: <span class="type">Data</span>, </span><br><span class="line">    <span class="params">using</span> <span class="params">secureElementPass</span>: <span class="type">PKSecureElementPass</span></span><br><span class="line">) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; (<span class="type">Data</span>, <span class="type">Data</span>)</span><br></pre></td></tr></table></figure><!--When the compiler sees a call to such a method, it effectively uses `withUnsafeContinuation` to form a continuation for the rest of the function, then wraps the given continuation in a closure. For example:--><p>当编译器看到对这种方法的调用时，它会使用 <code>withUnsafeContinuation</code> 为函数的其余部分生成一个 <code>continuation</code>，然后将给定的 <code>continuation</code> 封装进一个闭包里。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> (signedValue, signature) <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> passLibrary.sign(signData, using: pass)</span><br></pre></td></tr></table></figure><!--becomes pseudo-code similar to--><p>将成为类似于这样的代码：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">try</span> withUnsafeContinuation &#123; continuation <span class="keyword">in</span> </span><br><span class="line">    passLibrary.sign(</span><br><span class="line">        signData, using: pass, </span><br><span class="line">        completionHandler: &#123; (signedValue, signature, error) <span class="keyword">in</span></span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">let</span> error <span class="operator">=</span> error &#123;</span><br><span class="line">                continuation.resume(throwing: error)</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                continuation.resume(returning: (signedValue<span class="operator">!</span>, signature<span class="operator">!</span>))</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Additional rules are applied when translating an Objective-C method name into a Swift name of an `async` function:--><p>把 Objective-C 方法名翻译成 <code>async</code> 函数时，在 Swift 里的名称会有额外的规则：</p><!--* If the base name of the method starts with `get`, the `get` is removed and the leading initialisms are lowercased.* If the base name of the method ends with `Asynchronously`, that word is removed.--><ul><li>如果方法的名称以 <code>get</code> 开头，则去掉 <code>get</code>，<code>get</code> 后面的词改成小写。</li><li>如果方法的名称以 <code>Asynchronously</code> 结尾，则删除该词。</li></ul><!--If the completion-handler parameter of the Objective-C method is nullable and the translated `async` method returns non-`Void`, it will be marked with the `@discardableResult` attribute. For example:--><p>如果 Objective-C 方法的 completion-handler 参数是 optional 的，且翻译后的 <code>async</code> 方法返回类型不是 <code>Void</code>，则会用 <code>@discardableResult</code> 注解来标记。比如说：</p><figure class="highlight objectivec"><table><tr><td class="code"><pre><span class="line">-(<span class="type">void</span>)stopRecordingWithCompletionHandler:<span class="type">void</span>(^ _Nullable)(RPPreviewViewController * _Nullable, <span class="built_in">NSError</span> * _Nullable)handler;</span><br></pre></td></tr></table></figure><p>会成为：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@discardableResult</span> <span class="keyword">func</span> <span class="title function_">stopRecording</span>() <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">RPPreviewViewController</span></span><br></pre></td></tr></table></figure><h3 id="在-Swift-里定义异步-objc-方法"><a href="#在-Swift-里定义异步-objc-方法" class="headerlink" title="在 Swift 里定义异步 @objc 方法"></a>在 Swift 里定义异步 <code>@objc</code> 方法</h3><!--Many Swift entities can be exposed to Objective-C via the `@objc` attribute. With an `async` Swift method, the compiler will add an appropriate completion-handler parameter to the Objective-C method it creates, using what is effectively the inverse of the transformation described in the previous section, such that the Objective-C method produced is an asynchronous Objective-C completion-handler method. For example, a method such as:--><p>许多 Swift 符号可以通过 <code>@objc</code> 注解暴露给 Objective-C。有了 <code>async</code> Swift 方法，编译器将在它创建的 Objective-C 方法中添加一个相应的 completion-handler 参数，使用的是上一节中介绍的转换规则的反向版本，这样产生的 Objective-C 方法就是一个异步的 Objective-C completion-handler 方法。例如，一个类似于这样的方法：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">perform</span>(<span class="params">operation</span>: <span class="type">String</span>) <span class="keyword">async</span> -&gt; <span class="type">Int</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--will translate into the following Objective-C method:--><p>将翻译为这样的 Objective-C 方法：</p><figure class="highlight objectivec"><table><tr><td class="code"><pre><span class="line">- (<span class="type">void</span>)performWithOperation:(<span class="built_in">NSString</span> * _Nonnull)operation</span><br><span class="line">           completionHandler:(<span class="type">void</span> (^ _Nullable)(<span class="built_in">NSInteger</span>))completionHandler;</span><br></pre></td></tr></table></figure><!--The Objective-C method implementation synthesized by the compiler will create a detached task that calls the `async` Swift method `perform(operation:)` with the given string, then (if the completion handler argument is not `nil`) forwards the result to the completion handler.--><p>编译器合成的 Objective-C 方法实现将创建一个独立任务，这个任务会用传入的字符串调用 <code>async</code> Swift 方法 <code>perform(operation:)</code>，然后将结果转发给 completion-handler（如果 completion-handler 不是 <code>nil</code> 的话）。</p><!--For an `async throws` method, the completion handler is extended with an `NSError *` parameter to indicate the error, any non-nullable pointer type parameters are made `_Nullable`, and any nullable pointer type parameters are made `_Nullable_result`. For example, given:--><p>对于一个 <code>async throws</code> 方法，completion-handler 扩展了一个 <code>NSError *</code> 参数来表示 error，任何 non-nullable 的指针类型的参数都做成 <code>_Nullable</code>，任何 nullable 指针类型的参数都做成 <code>_Nullable_result</code>。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">performDangerousTrick</span>(<span class="params">operation</span>: <span class="type">String</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">String</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--the resulting Objective-C method will have the following signature:--><p>产生的 Objective-C 方法签名会是这样的：</p><figure class="highlight objectivec"><table><tr><td class="code"><pre><span class="line">- (<span class="type">void</span>)performDangerousTrickWithOperation:(<span class="built_in">NSString</span> * _Nonnull)operation</span><br><span class="line">    completionHandler:(<span class="type">void</span> (^ _Nullable)(<span class="built_in">NSString</span> * _Nullable, <span class="built_in">NSError</span> * _Nullable))completionHandler;</span><br></pre></td></tr></table></figure><!--Again, the synthesized Objective-C method implementation will create a detached task that calls the `async throws` method `performDangerousTrick(operation:)`. If the method returns normally, the `String` result will be delivered to the completion handler in the first parameter and the second parameter (`NSError *`) will be passed `nil`. If the method throws, the first parameter will be passed `nil` (which is why it has been made `_Nullable` despite being non-optional in Swift) and the second parameter will receive the error. If there are non-pointer parameters, they will be passed zero-initialized memory in the non-error arguments to provide consistent behavior for callers. This can be demonstrated with Swift pseudo-code:--><p>同样，合成的 Objective-C 方法实现将创建一个独立任务，调用 <code>async throws</code> 方法<code>performDangerousTrick(operation:)</code>。如果方法正常返回，那么 <code>String</code> 结果将在第一个参数中传入给 completion-handler，第二个参数（<code>NSError *</code>）将传入 <code>nil</code>。如果方法是 <code>throws</code> 的，第一个参数将被传入 <code>nil</code>（这就是为什么它被做成 <code>_Nullable</code>，尽管在 Swift 中是 non-optional 的），第二个参数将收到 error。如果有非指针参数，它们将在非错误参数中传递初始化为零的内存，为调用者提供一致的行为。这里可以用等效的 Swift 代码来帮助理解：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Synthesized by the compiler</span></span><br><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">performDangerousTrick</span>(</span><br><span class="line">    <span class="params">operation</span>: <span class="type">String</span>,</span><br><span class="line">    <span class="params">completionHandler</span>: ((<span class="type">String</span>?, <span class="type">Error</span>?) -&gt; <span class="type">Void</span>)<span class="operator">?</span></span><br><span class="line">) &#123;</span><br><span class="line">    runDetached &#123;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> value <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> performDangerousTrick(operation: operation)</span><br><span class="line">            completionHandler<span class="operator">?</span>(value, <span class="literal">nil</span>)</span><br><span class="line">        &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">            completionHandler<span class="operator">?</span>(<span class="literal">nil</span>, error)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Actor-类"><a href="#Actor-类" class="headerlink" title="Actor 类"></a>Actor 类</h3><!--Actor classes can be `@objc` and will be available in Objective-C as are other classes. Actor classes require that their superclass (if there is one) also be an actor class. However, this proposal loosens that requirement slightly to allow an actor class to have `NSObject` as its superclass. This is conceptually safe because `NSObject` has no state (and its layout is effectively fixed that way), and makes it possible both for actor classes to be `@objc` and also implies conformance to `NSObjectProtocol`, which is required when conforming to a number of Objective-C protocols and is otherwise unimplementable in Swift. --><p>Actor 类可以是 <code>@objc</code> 的，并将在 Objective-C 中和其他类一样可用。Actor 类要求其父类（如果有的话）也是 Actor 类。然而，这个提案稍微放宽了这个要求，允许一个 actor 类将 <code>NSObject</code> 作为它的父类。在理论上这是安全的，因为 <code>NSObject</code> 没有状态(而且它的布局实际上是固定的)，并且使得 actor 类既可以是 <code>@objc</code> 的，也意味着它遵循le1 <code>NSObjectProtocol</code>，这在实现一些 Objective-C 协议时是必要的，否则在 Swift 中是无法实现的。</p><!--A member of an actor class can only be `@objc` if it is either `async` or is outside of the actor's isolation domain. Synchronous code that is within the actor's isolation domain can only be invoked on `self` (in Swift). Objective-C does not have knowledge of actor isolation, so these members are not permitted to be exposed to Objective-C. For example:--><p>一个 actor 类的成员只有在它是 <code>async</code> 或在 actor 的隔离域之外时才能成为 <code>@objc</code>。在 actor 隔离域内的同步代码只能在 <code>self</code> 上被调用（在 Swift 中）。Objective-C 没有 actor 隔离的概念，所以这些成员是不允许暴露在 Objective-C 中的。比如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">actor</span> <span class="title class_">class</span> <span class="type">MyActor</span> &#123;</span><br><span class="line">    <span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">synchronous</span>() &#123; &#125; <span class="comment">// error: part of actor&#x27;s isolation domain</span></span><br><span class="line">    <span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">asynchronous</span>() <span class="keyword">async</span> &#123; &#125; <span class="comment">// okay: asynchronous</span></span><br><span class="line">    <span class="keyword">@objc</span> <span class="meta">@actorIndependent</span> <span class="keyword">func</span> <span class="title function_">independent</span>() &#123; &#125; <span class="comment">// okay: actor-independent</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="completion-handler-必须只调用一次"><a href="#completion-handler-必须只调用一次" class="headerlink" title="completion-handler 必须只调用一次"></a>completion-handler 必须只调用一次</h3><!--A Swift `async` function will always suspend, return, or (if it throws) produce an error. For completion-handler APIs, it is important that the completion handler block be called exactly once on all paths, including when producing an error. Failure to do so will break the semantics of the caller, either by failing to continue or by executing the same code multiple times. While this is an existing problem, widespread use of `async` with incorrectly-implemented completion-handler APIs might exacerbate the issue.--><p>一个 Swift <code>async</code> 函数总是会暂停、返回或（如果它是 throws 的话）抛出一个错误。对于 completion-handler API 来说，重要的是 completion-handler 在所有路径上都被准确地调用一次，包括抛出错误时。如果不这样做，就会破坏调用者的语义，要么不能继续，要么多次执行相同的代码。虽然这是一个目前就存在的问题，但广泛使用 <code>async</code> 和没有正确实现的 completion-handler 可能会加剧这个问题。</p><!--Fortunately, because the compiler itself is synthesizing the block that will be passed to completion-handler APIs, it can detect both problems by introducing an extra bit of state into the synthesized block to indicate that the block has been called. If the bit is already set when the block is called, then it has been called multiple times. If the bit is not set when the block is destroyed, it has not been called at all. While this does not fix the underlying problem, it can at least detect the issue consistently at run time.--><p>幸运的是，由于编译器本身会负责生成将被传递给 completion-handler API 的闭包，它可以通过在合成的闭包中引入一个额外的标志位来检测这两个问题，表明这个闭包是否已经被调用过。如果当闭包被调用时，这个标志位有值了，那么就证明它已经被多次调用。如果这个标志位在闭包被销毁时没有被设置过，则说明它根本没有被调用过。虽然这并不能解决根本问题，但至少可以在运行时检测出问题。</p><h3 id="新增的-Objective-C-注解"><a href="#新增的-Objective-C-注解" class="headerlink" title="新增的 Objective-C 注解"></a>新增的 Objective-C 注解</h3><!--The transformation of Objective-C completion-handler-based APIs to async Swift APIs could benefit from the introduction of additional annotations (in the form of attributes) to guide the process. For example:--><p>将基于 Objective-C completion-handler 的 API 转化为 async Swift API 时，会引入额外的标注（以注解的形式）来定制和优化这个过程。例如：</p><!--* `_Nullable_result`. Like `_Nullable`, indicates that a pointer can be null (or `nil`). `_Nullable_result` differs from `_Nullable` only for parameters to completion handler blocks. When the completion handler block's parameters are translated into the result type of an `async` method, the corresponding result will be optional.* `__attribute__((swift_async(...)))`. An attribute to control the translation of an asynchronous completion-handler method to an `async` function. It has several operations within the parentheses:  * `__attribute__((swift_async(none)))`. Disables the translation to `async`.    * `__attribute__((swift_async(not_swift_private, C)))`. Specifies that the method should be translated into an `async` method, using the parameter at index `C` as the completion handler parameter. The first (non-`self`) parameter has index 1.  * `__attribute__((swift_async(swift_private, C)))`. Specifies that the method should be translated into an `async` method that is "Swift private" (only for use when wrapping), using the parameter at index `C` as the completion handler parameter. The first (non-`self`) parameter has index 1.* `__attribute__((swift_attr("swift attribute")))`. A general-purpose Objective-C attribute to allow one to provide Swift attributes directly. In the context of concurrency, this allows Objective-C APIs to be annotated with a global actor (e.g., `@UIActor`).* `__attribute__((swift_async_name("method(param1:param2:)")))`. Specifies the Swift name that should be used for the `async` translation of the API. The name should not include an argument label for the completion handler parameter.* `__attribute__((swift_async_error(...)))`. An attribute to control how passing an `NSError *` into the completion handle maps into the method being `async throws`. It has several possible parameters:  * `__attribute__((swift_async_error(none)))`: Do not import as `throws`. The `NSError *` parameter will be considered a normal parameter.  * `__attribute__((swift_async_error(zero_argument(N)))`: Import as `throws`. When the Nth argument to the completion handler is passed the integral value zero (including `false`), the async method will throw the error. The Nth argument is removed from the result type of the translated `async` method. The first argument is `0`.  * `__attribute__((swift_async_error(nonzero_argument(N)))`: Import as `throws`. When the Nth argument to the completion handler is passed a non-zero integral value (including `true`), the async method will throw the error. The Nth argument is removed from the result type of the translated `async` method.--><ul><li><code>_Nullable_result</code>：与 <code>_Nullable</code> 一样，表示指针可以是 null 的（或 <code>nil</code>）。<code>_Nullable_result</code> 与 <code>_Nullable</code> 的不同之处只在于 completion-handler 参数。当 completion-handler 的参数被转换为 <code>async</code> 方法的结果类型时，相应的结果将会是 optional 的。</li><li><code>__attribute__((swift_async(...)))</code>：用于控制如何将异步 completion-handler 翻译成 <code>async</code> 函数的注解。它在括号内有这几种操作：<ul><li><code>__attribute__((swift_async(none)))</code>：禁用翻译为 <code>async</code>。 </li><li><code>__attribute__((swift_async(not_swift_private, C)))</code>：指定该方法应该被翻译成 <code>async</code> 方法，使用索引 <code>C</code> 的参数作为 completion-handler 参数。第一个(非 <code>self</code>)参数的序号为 1。</li><li><code>__attribute__((swift_async(swift_private, C)))</code>：将该方法翻译成 “Swift private” 的 <code>async</code> 方法(仅在封装时使用)，使用索引 <code>C</code> 的参数作为 completion-handler 参数。第一个(非 <code>self</code>)参数的序号为 1。</li></ul></li><li><code>__attribute__((swift_attr(&quot;swift attribute&quot;)))</code>：一个通用 Objective-C 注解，允许大家直接提供 Swift 属性。在并发的上下文中，这允许 Objective-C API 被注解为一个全局 actor(例如，<code>@UIActor</code>)。</li><li><code>__attribute__((swift_async_name(&quot;method(param1:param2:)&quot;))</code>：指定翻译后的 <code>async</code> 函数的 Swift 名称。该名称不应包括 completion-handler 参数的参数标签。</li><li><code>__attribute__((swift_async_error(...)))</code>。一个注解，用于控制如何将 <code>NSError *</code> 传递给 completion-handler 并且映射 <code>async throws</code> 的方法中。它有几个可用的参数：<ul><li><code>__attribute__((swift_async_error(none)))</code>：不要导入为 <code>throws</code>。<code>NSError *</code> 参数将被视为正常参数。</li><li><code>__attribute__((swift_async_error(zero_argument(N)))</code>：导入为 <code>throws</code>。当 completion-handler 的第 n 个参数传递了一个为 0 的整数值（包括 <code>false</code>）时，async 方法将抛出错误。第 n 个参数会从翻译后的 <code>async</code> 方法的结果类型中删除。第一个参数的序号是 <code>1</code>。</li><li><code>__attribute__((swift_async_error(nonzero_argument(N)))</code>：导入为 <code>throws</code>。当完成处理程序的第 n 个参数被传递了一个非 0 的整数值（包括 <code>true</code>）时，async 方法将抛出错误。第 n 个参数将从翻译后的 <code>async</code> 方法的结果类型中删除。</li></ul></li></ul><h2 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a>代码兼容性</h2><!--Generally speaking, changes to the way in which Objective-C APIs are translated into Swift are source-breaking changes. To avoid breaking source compatibility, this proposal involves translating Objective-C asynchronous completion-handler methods as *both* their original completion-handler signatures and also with the new `async` signature. This allows existing Swift code bases to gradually adopt the `async` forms of API, rather than forcing (e.g.) an entire Swift module to adopt `async` all at once.--><p>一般来说，将 Objective-C API 翻译成 Swift 的规则修改是代码破坏性修改。为了避免破坏源码兼容性，这个提案会将 Objective-C 异步 completion-handler 方法翻译成 completion-handler 版本<strong>以及</strong>新的 <code>async</code> 版本。让现有的 Swift 代码库能够渐进地采用 <code>async</code> 形式的 API，而不是强迫（例如）整个 Swift 模块全部换成 <code>async</code>。</p><!--Importing the same Objective-C API in two different ways causes some issues:--><p>同时以两种不同的方式导入相同的 Objective-C API 会导致一些问题：</p><!--* Overloading of synchronous and asynchronous APIs. Objective-C frameworks may have evolved to include both synchronous and asynchronous versions of the same API, e.g.,--><ul><li><p>同步和异步 API 的重载。Objective-C 框架可能已经同时包含同一个 API 的同步和异步版本，例如：</p><figure class="highlight objectivec"><table><tr><td class="code"><pre><span class="line">- (<span class="built_in">NSString</span> *)lookupName;</span><br><span class="line">- (<span class="type">void</span>)lookupNameWithCompletionHandler:(<span class="type">void</span> (^)(<span class="built_in">NSString</span> *))completion;</span><br></pre></td></tr></table></figure><!--which will be translated into three different Swift methods:--><p>会被翻译为三个不同的 Swift 方法：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">lookupName</span>() -&gt; <span class="type">String</span></span><br><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">lookupName</span>(<span class="params">withCompletionHandler</span>: <span class="keyword">@escaping</span> (<span class="type">String</span>) -&gt; <span class="type">Void</span>)</span><br><span class="line"><span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">lookupName</span>() <span class="keyword">async</span> -&gt; <span class="type">String</span></span><br></pre></td></tr></table></figure><!--The first and third signatures are identical except for being synchronous and asynchronous, respectively. The async/await design doesn't allow such overloading to be written in the same Swift module, but it can happen when translating Objective-C APIs or when importing methods from different Swift modules. The async/await design accounts for such overloading by favoring synchronous functions in synchronous contexts and asynchronous functions in asynchronous contexts. This overloading should avoid breaking source compatibility.-->  <p>第一个和第三个的函数签名，除了一个是同步一个是异步之外，其他都是一样的。async&#x2F;await 的设计不允许在同一个 Swift 模块中编写这样的重载，但在翻译 Objective-C API 或从不同 Swift 模块中导入方法时，可能会发生这样的情况。async&#x2F;await 会在同步上下文中有倾向性地选择同步函数，在异步上下文中有倾向性地选择异步函数，解决这种重载问题。这种重载应该避免破坏源码兼容性。</p></li></ul><!--* Another issue is when an asynchronous completion-handler method is part of an Objective-C protocol. For example, the [`NSURLSessionDataDelegate` protocol](https://developer.apple.com/documentation/foundation/nsurlsessiondatadelegate?language=objc) includes this protocol requirement:--><ul><li><p>另一个问题是当一个异步 completion-handler 方法是 Objective-C 协议的一部分。例如，<a href="https://developer.apple.com/documentation/foundation/nsurlsessiondatadelegate?language=objc"><code>NSURLSessionDataDelegate</code> 协议</a>就包括了这种协议要求:</p><figure class="highlight objectivec"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@optional</span></span><br><span class="line">- (<span class="type">void</span>)URLSession:(<span class="built_in">NSURLSession</span> *)session</span><br><span class="line">          dataTask:(<span class="built_in">NSURLSessionDataTask</span> *)dataTask</span><br><span class="line">didReceiveResponse:(<span class="built_in">NSURLResponse</span> *)response</span><br><span class="line"> completionHandler:(<span class="type">void</span> (^)(<span class="built_in">NSURLSessionResponseDisposition</span> disposition))completionHandler;</span><br></pre></td></tr></table></figure><!--Existing Swift code might implement this requirement in a conforming type using its completion-handler signature--><p>现有的 Swift 代码可能会在 conformance 里实现它的 completion-handler 版本：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">urlSession</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">session</span>: <span class="type">URLSession</span>,</span><br><span class="line">    <span class="params">dataTask</span>: <span class="type">URLSessionDataTask</span>,</span><br><span class="line">    <span class="params">didReceive</span> <span class="params">response</span>: <span class="type">URLResponse</span>,</span><br><span class="line">    <span class="params">completionHandler</span>: <span class="keyword">@escaping</span> (<span class="type">URLSession</span>.<span class="type">ResponseDisposition</span>) -&gt; <span class="type">Void</span></span><br><span class="line">) &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--while Swift code designed to take advantage of the concurrency model would implement this requirement in a conforming type using its `async` signature-->  <p>而为使用并发模型而设计的 Swift 代码，可能会选择在 conformance 中实现它的 <code>async</code>  版本：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@objc</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">urlSession</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">session</span>: <span class="type">URLSession</span>,</span><br><span class="line">    <span class="params">dataTask</span>: <span class="type">URLSessionDataTask</span>,</span><br><span class="line">    <span class="params">didReceive</span> <span class="params">response</span>: <span class="type">URLResponse</span></span><br><span class="line">) <span class="keyword">async</span> -&gt; <span class="type">URLSession</span>.<span class="type">ResponseDisposition</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--Implementing both requirements would produce an error (due to two Swift methods having the same selector), but under the normal Swift rules implementing only one of the requirements will also produce an error (because the other requirement is unsatisfied). Swift’s checking of protocol conformances will be extended to handle the case where multiple (imported) requirements have the same Objective-C selector: in that case, only one of them will be required to be implemented.-->  <p>同时实现这两个版本会产生一个错误（这两个 Swift 方法有相同的 selector），但是根据正常的 Swift 规则，只实现其中一个要求也会产生错误（因为另一个需求不满足）。Swift 对协议 conformance 的检查将会被扩展，以处理多个（导入的）要求有相同的 Objective-C selector 的情况：在这种情况下，只需要实现其中的一个要求。</p></li></ul><!--* Overriding methods that have been translated into both completion-handler and `async` versions have a similar problem to protocol requirements: a Swift subclass can either override the completion-handler version or the `async` version, but not both. Objective-C callers will always call to the subclass version of the method, but Swift callers to the "other" signature will not unless the subclass's method is marked with `@objc dynamic`. Swift can infer that the `async` overrides of such methods are `@objc dynamic` to avoid this problem (because such `async` methods are new code). However, inferring `@objc dynamic` on existing completion-handler overrides can change the behavior of programs and break subclasses of the subclasses, so at best the compiler can warn about this situation.--><ul><li>override 已被翻译成 completion-handler 和 <code>async</code> 版本的方法有一个类似于协议要求的问题：Swift 子类可以 override completion-handler 的版本或 <code>async</code> 的版本，但不能同时 override 这两个版本。Objective-C 的调用者总是会调用子类版本的方法，但 Swift 的调用者在调用另一个签名的函数时则不会，除非子类的方法被标记为 <code>@objc dynamic</code>。Swift可以将这类方法的 <code>async</code> override 隐式标记为 <code>@objc dynamic</code>，以避免这个问题（因为这类 <code>async</code> 方法是新代码）。但是，在现有的 completion-handler override 上将它推导为 <code>@objc dynamic</code> 会改变程序的行为，并破坏子类的子类，所以编译器最多只能对这种情况发出警告。</li></ul><h2 id="修订历史"><a href="#修订历史" class="headerlink" title="修订历史"></a>修订历史</h2><ul><li><p>Post-review:</p><ul><li><code>await try</code> becomes <code>try await</code> based on result of SE-0296 review</li><li>Added inference of <code>@discardableResult</code> for <code>async</code> methods translated from completion-handler methods with an optional completion handler.</li></ul></li><li><p>Changes in the second pitch:</p><ul><li>Removed mention of asynchronous handlers, which will be in a separate proposal.</li><li>Introduced the <code>swift_async_error</code> Clang attribute to separate out “throwing” behavior from the <code>swift_async</code> attribute.</li><li>Added support for “Swift private” to the <code>swift_async</code> attribute.</li><li>Tuned the naming heuristics based on feedback to add (e.g) <code>reply</code>, <code>replyTo</code>, <code>completionBlock</code>, and variants.</li><li>For the rare case where we match a parameter suffix, append the text prior to the suffix to the base name.</li><li>Replaced the <code>-generateCGImagesAsynchronouslyForTimes:completionHandler:</code> example with one from PassKit.</li><li>Added a “Future Directions” section about <code>NSProgress</code>.</li></ul></li><li><p>Original pitch (<a href="https://github.com/DougGregor/swift-evolution/blob/9b9bdfd16eb5ced390913ea170007a46eabb08eb/proposals/NNNN-concurrency-objc.md">document</a> and <a href="https://forums.swift.org/t/concurrency-interoperability-with-objective-c/41616">forum thread</a>).</p></li></ul><h2 id="改进方向"><a href="#改进方向" class="headerlink" title="改进方向"></a>改进方向</h2><h3 id="NSProgress"><a href="#NSProgress" class="headerlink" title="NSProgress"></a>NSProgress</h3><!--Some Objective-C completion-handler methods return an [NSProgress](https://developer.apple.com/documentation/foundation/progress) to allow the caller to evaluate progress of the asynchronous operation. Such methods are *not* imported as `async` in this proposal, because the method does not return `void`. For example:--><p>一些 Objective-C completion-handler 方法会返回一个 <a href="https://developer.apple.com/documentation/foundation/progress">NSProgress</a>，以便调用者评估异步操作的进度。在本提案中，这类方法<strong>不回</strong>被导入为 <code>async</code> 版本，因为该方法不返回 <code>void</code>。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="operator">-</span> (<span class="type">NSProgress</span> <span class="operator">*</span>)doSomethingThatTakesALongTimeWithCompletionHandler:(void (<span class="operator">^</span>)(<span class="type">MyResult</span> <span class="operator">*</span> _Nullable, <span class="type">NSError</span> <span class="operator">*</span> _Nullable))completionHandler;</span><br></pre></td></tr></table></figure><!--To support such methods would require some kind of integration between `NSProgress` and Swift's tasks. For example, when calling such a method, the `NSProgress` returned from such a call to be recorded in the task (say, in some kind of task-local storage). The other direction, where a Swift-defined method overrides a method, would need to extract an `NSProgress` from the task to return. Such a design is out of scope for this proposal, but could be introduced at some later point.--><p>要支持这种方法，就需要在 <code>NSProgress</code> 和 Swift 的任务之间进行某种整合。例如，当调用这样的方法时，从这样的调用中返回的 <code>NSProgress</code> 要记录在任务中（例如，在某种 task-local 的存储中）。还有另一个方向是，Swift 定义的方法 override 一个方法时，则需要从任务中提取一个 <code>NSProgress</code> 来返回。这样的设计不在本提案的范围内，但可以在以后的某个阶段引入。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2021-03-07/</id>
    <link href="https://kemchenj.github.io/2021-03-07/"/>
    <published>2021-03-06T16:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md">SE-0297 Concurrency Interoperability with Objective-C</a></p>
</blockquote>
<ul>
<li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0297-concurrency-objc.md">SE-0297</a></li>
<li>Author: <a href="https://github.com/DougGregor">Doug Gregor</a></li>
<li>Review Manager: <a href="https://github.com/lattner">Chris Lattner</a></li>
<li>Status: <strong>Implemented (Swift 5.5)</strong></li>
<li><a href="https://forums.swift.org/t/accepted-with-modifications-se-0297-concurrency-interoperability-with-objective-c/43306">Acceptance Post</a></li>
<li>Implementation: Partially available in <a href="https://swift.org/download/#snapshots">recent <code>main</code> snapshots</a> behind the flag <code>-Xfrontend -enable-experimental-concurrency</code></li>
</ul>
<!--
## Table of Contents

* [Introduction](#introduction)
* [Motivation](#motivation)
* [Proposed solution](#proposed-solution)
* [Detailed design](#detailed-design)
   * [Asynchronous completion-handler methods](#asynchronous-completion-handler-methods)
   * [Defining asynchronous @objc methods in Swift](#defining-asynchronous-objc-methods-in-swift)
   * [Actor classes](#actor-classes)
   * [Completion handlers must be called exactly once](#completion-handlers-must-be-called-exactly-once)
   * [Additional Objective-C attributes](#additional-objective-c-attributes)
* [Source compatibility](#source-compatibility)
* [Revision history](#revision-history)
* [Future Directions](#future-directions)
   * [NSProgress](#nsprogress)
-->

<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--
Swift's concurrency feature involves asynchronous functions and actors. While Objective-C does not have corresponding language features, asynchronous APIs are common in Objective-C, expressed manually through the use of completion handlers. This proposal provides bridging between Swift's concurrency features (e.g., `async` functions) and the convention-based expression of asynchronous functions in Objective-C. It is intended to allow the wealth of existing asynchronous Objective-C APIs to be immediately usable with Swift's concurrency model.
-->

<p>Swift 的并发功能包括了异步函数和 actor。虽然 Objective-C 没有相应的语言特性，但异步 API 在 Objective-C 中很常见，通过使用 completion-handler 手动实现。本提案提供了 Swift 的并发特性（如 <code>async</code> 函数）和 Objective-C 中基于约定的异步函数表达之间的桥接。它的目的是让现有的丰富的异步 Objective-C API 可以立即与 Swift 的并发模型一起使用。</p>]]>
    </summary>
    <title>【译】SE-0297 Concurrency 与 Objective-C 的交互</title>
    <updated>2026-09-02T11:08:19.193Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<blockquote><p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md">SE-0296 async&#x2F;await</a></p></blockquote><ul><li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md">SE-0296</a></li><li>Authors: <a href="https://github.com/rjmccall">John McCall</a>, <a href="https://github.com/DougGregor">Doug Gregor</a></li><li>Review Manager: <a href="https://github.com/airspeedswift">Ben Cohen</a></li><li>Status: <strong>Implemented (Swift 5.5)</strong></li><li>Implementation: Available in <a href="https://swift.org/download/#snapshots">recent <code>main</code> snapshots</a> behind the flag <code>-Xfrontend -enable-experimental-concurrency</code></li><li>Decision Notes: <a href="https://forums.swift.org/t/accepted-with-modification-se-0296-async-await/43318">Rationale</a></li></ul><!--Table of Contents=================   * [Async/await](#asyncawait)      * [Introduction](#introduction)      * [Motivation: Completion handlers are suboptimal](#motivation-completion-handlers-are-suboptimal)      * [Proposed solution: async/await](#proposed-solution-asyncawait)         * [Suspension points](#suspension-points)      * [Detailed design](#detailed-design)         * [Asynchronous functions](#asynchronous-functions)         * [Asynchronous function types](#asynchronous-function-types)         * [Await expressions](#await-expressions)         * [Closures](#closures)         * [Overloading and overload resolution](#overloading-and-overload-resolution)         * [Autoclosures](#autoclosures)         * [Protocol conformance](#protocol-conformance)      * [Source compatibility](#source-compatibility)      * [Effect on ABI stability](#effect-on-abi-stability)      * [Effect on API resilience](#effect-on-api-resilience)      * [Future Directions](#future-directions)         * [reasync](#reasync)      * [Alternatives Considered](#alternatives-considered)         * [Make await imply try](#make-await-imply-try)         * [Launching async tasks](#launching-async-tasks)         * [Await as syntactic sugar](#await-as-syntactic-sugar)      * [Revision history](#revision-history)      * [Related proposals](#related-proposals)      * [Acknowledgments](#acknowledgments)--><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--Modern Swift development involves a lot of asynchronous (or "async") programming using closures and completion handlers, but these APIs are hard to use.  This gets particularly problematic when many asynchronous operations are used, error handling is required, or control flow between asynchronous calls gets complicated.  This proposal describes a language extension to make this a lot more natural and less error prone.--><p>现代 Swift 开发涉及大量使用闭包和回调的异步编程，但这些 API 很难使用。当使用了许多异步操作，需要错误处理，或者异步调用之间的控制流变得复杂时，这就变得特别麻烦。这个提案描述了一种语言扩展，使之更自然，更不容易出错。</p><!--This design introduces a [coroutine model](https://en.wikipedia.org/wiki/Coroutine) to Swift. Functions can opt into being `async`, allowing the programmer to compose complex logic involving asynchronous operations using the normal control-flow mechanisms. The compiler is responsible for translating an asynchronous function into an appropriate set of closures and state machines.--><p>这份设计将 <a href="https://en.wikipedia.org/wiki/Coroutine">coroutine模型</a>引入 Swift。函数可以选择成为 <code>async</code>，允许程序员使用正常的控制流机制来编写涉及异步操作的复杂逻辑。编译器负责将一个异步函数翻译成一套适当的闭包和状态机。</p><!--This proposal defines the semantics of asynchronous functions. However, it does not provide concurrency: that is covered by a separate proposal to introduce structured concurrency, which associates asynchronous functions with concurrently-executing tasks and provides APIs for creating, querying, and cancelling tasks.--><p>这个提案定义了异步函数的语义。然而，它并没有提供并发性：这在另一个引入结构化并发的提案里讨论，该提案将异步函数与并发执行的任务联系起来，并提供用于创建、查询和取消任务的 API。</p><p>Swift-evolution thread: <a href="https://forums.swift.org/t/concurrency-asynchronous-functions/41619">Pitch #1</a>, <a href="https://forums.swift.org/t/pitch-2-async-await/42420">Pitch #2</a></p><span id="more"></span><h2 id="动机：闭包不是最理想的解决方案"><a href="#动机：闭包不是最理想的解决方案" class="headerlink" title="动机：闭包不是最理想的解决方案"></a>动机：闭包不是最理想的解决方案</h2><!--Async programming with explicit callbacks (also called completion handlers) has many problems, which we’ll explore below.  We propose to address these problems by introducing async functions into the language.  Async functions allow asynchronous code to be written as straight-line code.  They also allow the implementation to directly reason about the execution pattern of the code, allowing callbacks to run far more efficiently.--><p>使用显式回调的异步编程有很多问题，我们将在下面探讨这些问题。我们建议通过在语言中引入 <code>async</code> 函数来解决这些问题。<code>async</code> 函数允许将异步代码写成线性代码。它们还允许直接推导代码的执行模式，使回调的运行效率大大提高。</p><h4 id="问题-1：回调地狱"><a href="#问题-1：回调地狱" class="headerlink" title="问题 1：回调地狱"></a>问题 1：回调地狱</h4><!--A sequence of simple asynchronous operations often requires deeply-nested closures. Here is a made-up example showing this:--><p>想要把简单的异步操作串联起来往往需要嵌套多层闭包。下面举一个例子：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData1</span>(<span class="params">completionBlock</span>: (<span class="keyword">_</span> result: <span class="type">Image</span>) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>) &#123; dataResource <span class="keyword">in</span></span><br><span class="line">        loadWebResource(<span class="string">&quot;imagedata.dat&quot;</span>) &#123; imageResource <span class="keyword">in</span></span><br><span class="line">            decodeImage(dataResource, imageResource) &#123; imageTmp <span class="keyword">in</span></span><br><span class="line">                dewarpAndCleanupImage(imageTmp) &#123; imageResult <span class="keyword">in</span></span><br><span class="line">                    completionBlock(imageResult)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">processImageData1 &#123; image <span class="keyword">in</span></span><br><span class="line">    display(image)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This "pyramid of doom" makes it difficult to read and keep track of where the code is running. In addition, having to use a stack of closures leads to many second order effects that we will discuss next.--><p>这种“回调地狱”使得我们很难读取和跟踪代码运行的位置。此外，不得不使用闭包嵌套会导致许多副作用，我们将在接下来讨论。</p><h4 id="问题-2：错误处理"><a href="#问题-2：错误处理" class="headerlink" title="问题 2：错误处理"></a>问题 2：错误处理</h4><!--Callbacks make error handling difficult and very verbose. Swift 2 introduced an error handling model for synchronous code, but callback-based interfaces do not derive any benefit from it:--><p>回调使错误处理变得困难且非常啰嗦。Swift 2 为同步代码引入了一个错误处理模型，但基于回调的接口并没有从中得到任何好处:</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// (2a) Using a `guard` statement for each callback:</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData2a</span>(<span class="params">completionBlock</span>: (<span class="keyword">_</span> result: <span class="type">Image</span>?, <span class="keyword">_</span> error: <span class="type">Error</span>?) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>) &#123; dataResource, error <span class="keyword">in</span></span><br><span class="line">        <span class="keyword">guard</span> <span class="keyword">let</span> dataResource <span class="operator">=</span> dataResource <span class="keyword">else</span> &#123;</span><br><span class="line">            completionBlock(<span class="literal">nil</span>, error)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        &#125;</span><br><span class="line">        loadWebResource(<span class="string">&quot;imagedata.dat&quot;</span>) &#123; imageResource, error <span class="keyword">in</span></span><br><span class="line">            <span class="keyword">guard</span> <span class="keyword">let</span> imageResource <span class="operator">=</span> imageResource <span class="keyword">else</span> &#123;</span><br><span class="line">                completionBlock(<span class="literal">nil</span>, error)</span><br><span class="line">                <span class="keyword">return</span></span><br><span class="line">            &#125;</span><br><span class="line">            decodeImage(dataResource, imageResource) &#123; imageTmp, error <span class="keyword">in</span></span><br><span class="line">                <span class="keyword">guard</span> <span class="keyword">let</span> imageTmp <span class="operator">=</span> imageTmp <span class="keyword">else</span> &#123;</span><br><span class="line">                    completionBlock(<span class="literal">nil</span>, error)</span><br><span class="line">                    <span class="keyword">return</span></span><br><span class="line">                &#125;</span><br><span class="line">                dewarpAndCleanupImage(imageTmp) &#123; imageResult, error <span class="keyword">in</span></span><br><span class="line">                    <span class="keyword">guard</span> <span class="keyword">let</span> imageResult <span class="operator">=</span> imageResult <span class="keyword">else</span> &#123;</span><br><span class="line">                        completionBlock(<span class="literal">nil</span>, error)</span><br><span class="line">                        <span class="keyword">return</span></span><br><span class="line">                    &#125;</span><br><span class="line">                    completionBlock(imageResult)</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">processImageData2a &#123; image, error <span class="keyword">in</span></span><br><span class="line">    <span class="keyword">guard</span> <span class="keyword">let</span> image <span class="operator">=</span> image <span class="keyword">else</span> &#123;</span><br><span class="line">        display(<span class="string">&quot;No image today&quot;</span>, error)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    &#125;</span><br><span class="line">    display(image)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The addition of [`Result`](https://github.com/apple/swift-evolution/blob/main/proposals/0235-add-result.md) to the standard library improved on error handling for Swift APIs. Asynchronous APIs were one of the [main motivators](https://github.com/apple/swift-evolution/blob/main/proposals/0235-add-result.md#asynchronous-apis) for `Result`: --><p><a href="https://github.com/apple/swift-evolution/blob/main/proposals/0235-add-result.md"><code>Result</code></a> 加入标准库主要是为了改善 Swift API 的错误处理，而异步 API 是 <code>Result</code> 提案<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0235-add-result.md#asynchronous-apis">想要优化</a>的其中一部分：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// (2b) Using a `do-catch` statement for each callback:</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData2b</span>(<span class="params">completionBlock</span>: (<span class="type">Result</span>&lt;<span class="type">Image</span>, <span class="type">Error</span>&gt;) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>) &#123; dataResourceResult <span class="keyword">in</span></span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            <span class="keyword">let</span> dataResource <span class="operator">=</span> <span class="keyword">try</span> dataResourceResult.get()</span><br><span class="line">            loadWebResource(<span class="string">&quot;imagedata.dat&quot;</span>) &#123; imageResourceResult <span class="keyword">in</span></span><br><span class="line">                <span class="keyword">do</span> &#123;</span><br><span class="line">                    <span class="keyword">let</span> imageResource <span class="operator">=</span> <span class="keyword">try</span> imageResourceResult.get()</span><br><span class="line">                    decodeImage(dataResource, imageResource) &#123; imageTmpResult <span class="keyword">in</span></span><br><span class="line">                        <span class="keyword">do</span> &#123;</span><br><span class="line">                            <span class="keyword">let</span> imageTmp <span class="operator">=</span> <span class="keyword">try</span> imageTmpResult.get()</span><br><span class="line">                            dewarpAndCleanupImage(imageTmp) &#123; imageResult <span class="keyword">in</span></span><br><span class="line">                                completionBlock(imageResult)</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">                            completionBlock(.failure(error))</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">                    completionBlock(.failure(error))</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">            completionBlock(.failure(error))</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">processImageData2b &#123; result <span class="keyword">in</span></span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> image <span class="operator">=</span> <span class="keyword">try</span> result.get()</span><br><span class="line">        display(image)</span><br><span class="line">    &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">        display(<span class="string">&quot;No image today&quot;</span>, error)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// (2c) Using a `switch` statement for each callback:</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData2c</span>(<span class="params">completionBlock</span>: (<span class="type">Result</span>&lt;<span class="type">Image</span>, <span class="type">Error</span>&gt;) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>) &#123; dataResourceResult <span class="keyword">in</span></span><br><span class="line">        <span class="keyword">switch</span> dataResourceResult &#123;</span><br><span class="line">        <span class="keyword">case</span> .success(<span class="keyword">let</span> dataResource):</span><br><span class="line">            loadWebResource(<span class="string">&quot;imagedata.dat&quot;</span>) &#123; imageResourceResult <span class="keyword">in</span></span><br><span class="line">                <span class="keyword">switch</span> imageResourceResult &#123;</span><br><span class="line">                <span class="keyword">case</span> .success(<span class="keyword">let</span> imageResource):</span><br><span class="line">                    decodeImage(dataResource, imageResource) &#123; imageTmpResult <span class="keyword">in</span></span><br><span class="line">                        <span class="keyword">switch</span> imageTmpResult &#123;</span><br><span class="line">                        <span class="keyword">case</span> .success(<span class="keyword">let</span> imageTmp):</span><br><span class="line">                            dewarpAndCleanupImage(imageTmp) &#123; imageResult <span class="keyword">in</span></span><br><span class="line">                                completionBlock(imageResult)</span><br><span class="line">                            &#125;</span><br><span class="line">                        <span class="keyword">case</span> .failure(<span class="keyword">let</span> error):</span><br><span class="line">                            completionBlock(.failure(error))</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                <span class="keyword">case</span> .failure(<span class="keyword">let</span> error):</span><br><span class="line">                    completionBlock(.failure(error))</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">case</span> .failure(<span class="keyword">let</span> error):</span><br><span class="line">            completionBlock(.failure(error))</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">processImageData2c &#123; result <span class="keyword">in</span></span><br><span class="line">    <span class="keyword">switch</span> result &#123;</span><br><span class="line">    <span class="keyword">case</span> .success(<span class="keyword">let</span> image):</span><br><span class="line">        display(image)</span><br><span class="line">    <span class="keyword">case</span> .failure(<span class="keyword">let</span> error):</span><br><span class="line">        display(<span class="string">&quot;No image today&quot;</span>, error)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--It's easier to handle errors when using `Result`, but the closure-nesting problem remains.--><p>使用 <code>Result</code> 可以简化错误处理，但闭包嵌套的问题依然存在。</p><h4 id="问题3：选择性执行很难并且容易出错"><a href="#问题3：选择性执行很难并且容易出错" class="headerlink" title="问题3：选择性执行很难并且容易出错"></a>问题3：选择性执行很难并且容易出错</h4><!--Conditionally executing an asynchronous function is a huge pain. For example, suppose we need to "swizzle" an image after obtaining it. But, we sometimes have to make an asynchronous call to decode the image before we can swizzle. Perhaps the best approach to structuring this function is to write the swizzling code in a helper "continuation" closure that is conditionally captured in a completion handler, like this:--><p>选择性执行一个异步函数是一件非常痛苦的事情。例如，假设我们需要在获取到图像后进行 <code>swizzle</code>，但是，我们有时候不得不在 <code>swizzle</code> 之前触发异步调用去解码图片。也许这个函数最好的结构是使用一个 helper “continuation” 闭包，在回调的闭包里捕获它，就像这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData3</span>(<span class="params">recipient</span>: <span class="type">Person</span>, <span class="params">completionBlock</span>: (<span class="keyword">_</span> result: <span class="type">Image</span>) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> swizzle: (<span class="keyword">_</span> contents: <span class="type">Image</span>) -&gt; <span class="type">Void</span> <span class="operator">=</span> &#123;</span><br><span class="line">      <span class="comment">// ... continuation closure that calls completionBlock eventually</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> recipient.hasProfilePicture &#123;</span><br><span class="line">        swizzle(recipient.profilePicture)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        decodeImage &#123; image <span class="keyword">in</span></span><br><span class="line">            swizzle(image)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This pattern inverts the natural top-down organization of the function: the code that will execute in the second half of the function must appear *before* the part that executes in the first half. In addition to restructuring the entire function, we must now think carefully about captures in the continuation closure, because the closure is used in a completion handler. The problem worsens as the number of conditionally-executed async functions grows, yielding what is essentially an inverted "pyramid of doom."--><p>这种模式颠倒了自上而下的自然函数结构：函数下半部才会执行的代码必须在上半部执行<em>之前</em>出现。为了重构这整个函数，我们必须小心地思考闭包里捕获的东西，因为这个闭包会在回调里被捕获。这个问题会随着选择性执行的异步函数越来越多变得越来越严重，最终会成为一个反转的回调地狱。</p><h4 id="问题4：很容易造成很多错误"><a href="#问题4：很容易造成很多错误" class="headerlink" title="问题4：很容易造成很多错误"></a>问题4：很容易造成很多错误</h4><!--It's quite easy to bail-out of the asynchronous operation early by simply returning without calling the correct completion-handler block. When forgotten, the issue is very hard to debug:--><p>异步操作提前退出时很容易忘记调用回调。忘记这件事的时候，问题就会很难 debug：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData4a</span>(<span class="params">completionBlock</span>: (<span class="keyword">_</span> result: <span class="type">Image</span>?, <span class="keyword">_</span> error: <span class="type">Error</span>?) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>) &#123; dataResource, error <span class="keyword">in</span></span><br><span class="line">        <span class="keyword">guard</span> <span class="keyword">let</span> dataResource <span class="operator">=</span> dataResource <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="comment">// &lt;- forgot to call the block</span></span><br><span class="line">        &#125;</span><br><span class="line">        loadWebResource(<span class="string">&quot;imagedata.dat&quot;</span>) &#123; imageResource, error <span class="keyword">in</span></span><br><span class="line">            <span class="keyword">guard</span> <span class="keyword">let</span> imageResource <span class="operator">=</span> imageResource <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="comment">// &lt;- forgot to call the block</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="operator">...</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--When you do remember to call the block, you can still forget to return after that:--><p>当你记得调用闭包时，你还有可能忘记在这之后 return：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData4b</span>(<span class="params">recipient</span>:<span class="type">Person</span>, <span class="params">completionBlock</span>: (<span class="keyword">_</span> result: <span class="type">Image</span>?, <span class="keyword">_</span> error: <span class="type">Error</span>?) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> recipient.hasProfilePicture &#123;</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">let</span> image <span class="operator">=</span> recipient.profilePicture &#123;</span><br><span class="line">            completionBlock(image) <span class="comment">// &lt;- forgot to return after calling the block</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Thankfully the `guard` syntax protects against forgetting to return to some degree, but it's not always relevant.--><p>还好 <code>guard</code> 语法会在一定程度上防止你忘记 return 的事情，但它不能解决所有问题。</p><h4 id="问题-5：因为回调很难用，很多-API-会设计成同步阻塞的形式"><a href="#问题-5：因为回调很难用，很多-API-会设计成同步阻塞的形式" class="headerlink" title="问题 5：因为回调很难用，很多 API 会设计成同步阻塞的形式"></a>问题 5：因为回调很难用，很多 API 会设计成同步阻塞的形式</h4><!--This is hard to quantify, but the authors believe that the awkwardness of defining and using asynchronous APIs (using completion handlers) has led to many APIs being defined with apparently synchronous behavior, even when they can block.  This can lead to problematic performance and responsiveness problems in UI applications, e.g. a spinning cursor.  It can also lead to the definition of APIs that cannot be used when asynchrony is critical to achieve scale, e.g. on the server.--><p>这很难量化，但作者认为，定义和使用异步 API（使用完成处理程序）的尴尬导致许多 API 被定义为明显的同步行为，即使它们会产生阻塞。这可能会导致 UI 应用程序的性能和响应性问题，例如旋转的光标。它还可能导致定义的 API 在异步对水平拓展至关重要时无法使用，例如在服务器上。</p><h2 id="解决方案：async-await"><a href="#解决方案：async-await" class="headerlink" title="解决方案：async&#x2F;await"></a>解决方案：async&#x2F;await</h2><!--Asynchronous functions—often known as async/await—allow asynchronous code to be written as if it were straight-line, synchronous code.  This immediately addresses many of the problems described above by allowing programmers to make full use of the same language constructs that are available to synchronous code.  The use of async/await also naturally preserves the semantic structure of the code, providing information necessary for at least three cross-cutting improvements to the language: (1) better performance for asynchronous code; (2) better tooling to provide a more consistent experience while debugging, profiling, and exploring code; and (3) a foundation for future concurrency features like task priority and cancellation.  The example from the prior section demonstrates how async/await drastically simplifies asynchronous code:--><p>异步函数 - 通常被称为 async&#x2F;await - 允许将异步代码当作线性同步代码来编写。这就立马解决了上述的许多问题，因为它允许程序员充分利用与同步代码相同的语言结构。使用 async&#x2F;await 还自然地保留了代码的语义结构，提供了必要的信息让语言可以做至少三个方向的改进。(1) 为异步代码提供更好的性能；(2) 更好的工具，在调试、剖析和探索代码时提供更一致的体验；(3) 为未来的并发特性（如任务优先级和取消）奠定基础。上一节的例子可以展示 async&#x2F;await 如何大幅简化异步代码：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">loadWebResource</span>(<span class="keyword">_</span> <span class="params">path</span>: <span class="type">String</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Resource</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">decodeImage</span>(<span class="keyword">_</span> <span class="params">r1</span>: <span class="type">Resource</span>, <span class="keyword">_</span> <span class="params">r2</span>: <span class="type">Resource</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Image</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">dewarpAndCleanupImage</span>(<span class="keyword">_</span> <span class="params">i</span> : <span class="type">Image</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Image</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">processImageData</span>() <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Image</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> dataResource  <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>)</span><br><span class="line">  <span class="keyword">let</span> imageResource <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> loadWebResource(<span class="string">&quot;imagedata.dat&quot;</span>)</span><br><span class="line">  <span class="keyword">let</span> imageTmp      <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> decodeImage(dataResource, imageResource)</span><br><span class="line">  <span class="keyword">let</span> imageResult   <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> dewarpAndCleanupImage(imageTmp)</span><br><span class="line">  <span class="keyword">return</span> imageResult</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>许多关于 async&#x2F;await 的描述讨论了一个通用的实现机制：一个将函数拆分为多个组件的编译器 pass。这对于底层抽象来说很重要，为了理解机器如何执行，但在更高的层次，我们更鼓励你忽略它。相反，把异步函数看成一个普通函数，只是具有放弃其线程的特殊权利。异步函数通常不会直接使用这个权力；相反，它们产生调用，有时这些调用会要求它们放弃自己的线程，等待某件事情的发生。当这个事情完成后，函数会再次恢复执行。</p><!--The analogy with synchronous functions is very strong.  A synchronous function can make a call; when it does, the function immediately waits for the call to complete. Once the call completes, control returns to the function and picks up where it left off.  The same thing is true with an asynchronous function: it can make calls as usual; when it does, it (normally) immediately waits for the call to complete. Once the call completes, control returns to the function and it picks up where it was.  The only difference is that synchronous functions get to take full advantage of (part of) their thread and its stack, whereas *asynchronous functions are able to completely give up that stack and use their own, separate storage*.  This additional power given to asynchronous functions has some implementation cost, but we can reduce that quite a bit by designing holistically around it.--><p>与同步函数对比非常明显。一个同步函数可以进行调用；当它调用时，函数立即等待调用完成。一旦调用完成，控制权就会返回到函数，并从它离开的地方开始。异步函数也是如此：它可以像往常一样进行调用；当它进行调用时，它（通常）立即等待调用完成。一旦调用完成，控制权就会回到函数，它就会回到原来的位置。唯一的区别是，同步函数可以充分利用（部分）它的线程和它的栈，而<em>异步函数则可以完全放弃这个栈，使用自己的、独立的存储</em>。这种赋予异步函数的额外权力有一定的实现成本，但我们可以通过围绕它进行整体设计来降低不少成本。</p><!--Because asynchronous functions must be able to abandon their thread, and synchronous functions don’t know how to abandon a thread, a synchronous function can’t ordinarily call an asynchronous function: the asynchronous function would only be able to give up the part of the thread it occupied, and if it tried, its synchronous caller would treat it like a return and try to pick up where it was, only without a return value.  The only way to make this work in general would be to block the entire thread until the asynchronous function was resumed and completed, and that would completely defeat the purpose of asynchronous functions, as well as having nasty systemic effects.--><p>因为异步函数必须能够放弃自己的线程，而同步函数不知道如何放弃一个线程，所以一个同步函数通常不能调用一个异步函数：异步函数只能放弃它所占用的那部分线程，如果它试图放弃，它的同步调用者就会把它当作一个返回，并试图拾起原来的位置，只是没有返回值。在一般情况下，唯一的办法就是阻塞整个线程，直到异步函数被恢复并完成，但这将完全违背异步函数的目的，同时也会产生恶劣的系统影响。</p><!--In contrast, an asynchronous function can call either synchronous or asynchronous functions.  While it’s calling a synchronous function, of course, it can’t give up its thread.  In fact, asynchronous functions never just spontaneously give up their thread; they only give up their thread when they reach what’s called a suspension point.  A suspension point can occur directly within a function, or it can occur within another asynchronous function that the function calls, but in either case the function and all of its asynchronous callers simultaneously abandon the thread.  (In practice, asynchronous functions are compiled to not depend on the thread during an asynchronous call, so that only the innermost function needs to do any extra work.)--><p>而异步函数则可以调用同步函数或异步函数。当然，当它在调用同步函数时，它不能放弃自己的线程。事实上，异步函数从来不会自发地放弃自己的线程，只有当它达到所谓的 suspension point 时才会放弃自己的线程。suspension point 可以直接发生在一个函数中，也可以发生在该函数调用的另一个异步函数中，但无论哪种情况，该函数及其所有异步调用者都会同时放弃该线程。(实际上，异步函数在异步调用过程中都会被编译成不依赖于线程，因此只有最内部的函数需要做任何额外的工作)。</p><!--When control returns to an asynchronous function, it picks up exactly where it was.  That doesn’t necessarily mean that it’ll be running on the exact same thread it was before, because the language doesn’t guarantee that after a suspension.  In this design, threads are mostly an implementation mechanism, not a part of the intended interface to concurrency.  However, many asynchronous functions are not just asynchronous: they’re also associated with specific actors (which are the subject of a separate proposal), and they’re always supposed to run as part of that actor.  Swift does guarantee that such functions will in fact return to their actor to finish executing.  Accordingly, libraries that use threads directly for state isolation—for example, by creating their own threads and scheduling tasks sequentially onto them—should generally model those threads as actors in Swift in order to allow these basic language guarantees to function properly.--><p>当控制返回到一个异步函数时，它就会从原来的地方拾起。这并不意味着它一定会在和之前完全相同的线程上运行，因为语言并不能保证在 suspension 后会这样。在这种设计中，线程主要是一种实现机制，而不是并发的预设接口的一部分。然而，许多异步函数并不仅仅是异步的：它们还与特定的 actor 相关联（这是一个单独提案的主题），而且它们总是应该作为该 actor 的一部分运行。Swift 确实保证这类函数事实上会返回到它们的 actor 中去完成执行。相应地，直接使用线程进行状态隔离的库–例如，通过创建自己的线程并将任务按顺序调度到线程上–一般应该将这些线程建模为 Swift 里的 actor，以便这些基本的语言保证能够正常运行。</p><h3 id="Suspension-point"><a href="#Suspension-point" class="headerlink" title="Suspension point"></a>Suspension point</h3><!--A suspension point is a point in the execution of an asynchronous function where it has to give up its thread.  Suspension points are always associated with some deterministic, syntactically explicit event in the function; they’re never hidden or asynchronous from the function’s perspective.  The primary form of suspension point is a call to an asynchronous function associated with a different execution context.--><p>Suspension point 是异步函数执行过程中不得不放弃其线程的一个点。Suspension point 总是与函数中的一些确定性的、语法上显式的事件相关联；从函数的角度来看，它们从来都不是隐藏的或异步的。Suspension point 的主要形式是调用一个与不同执行上下文相关联的异步函数。</p><!--It is important that suspension points are only associated with explicit operations.  In fact, it’s so important that this proposal requires that calls that *might* suspend be enclosed in an `await` expression. These calls are referred to as *potential suspension points*, because it is not known statically whether they will actually suspend: that depends both on code not visible at the call site (e.g., the callee might depend on asynchronous I/O) as well as dynamic conditions (e.g., whether that asynchronous I/O will have to wait to complete). --><p>重要的是，suspension point 只与显式操作相关联。事实上，这一点非常重要，以至于该提案要求将<em>可能</em> suspend 的调用用 <code>await</code> 表达式修饰。这些调用被称为<em>潜在的 suspension point</em>，因为静态分析时并不知道它们是否真的会 suspend：这既取决于调用方不可见的代码（例如，被调用者可能依赖于异步 I&#x2F;O），也取决于动态条件（例如，该异步 I&#x2F;O是否需要等待才能完成）。</p><!--The requirement for `await` on potential suspension points follows Swift's precedent of requiring `try` expressions to cover calls to functions that can throw errors. Marking potential suspension points is particularly important because *suspensions interrupt atomicity*.  For example, if an asynchronous function is running within a given context that is protected by a serial queue, reaching a suspension point means that other code can be interleaved on that same serial queue.  A classic but somewhat hackneyed example where this atomicity matters is modeling a bank: if a deposit is credited to one account, but the operation suspends before processing a matched withdrawal, it creates a window where those funds can be double-spent.  A more germane example for many Swift programmers is a UI thread: the suspension points are the points where the UI can be shown to the user, so programs that build part of their UI and then suspend risk presenting a flickering, partially-constructed UI.  (Note that suspension points are also called out explicitly in code using explicit callbacks: the suspension happens between the point where the outer function returns and the callback starts running.)  Requiring that all potential suspension points are marked allows programmers to safely assume that places without potential suspension points will behave atomically, as well as to more easily recognize problematic non-atomic patterns.--><p>对潜在的 suspension point 的 <code>await</code> 要求延续 Swift 的之前的做法，要求 <code>try</code> 表达式涵盖对可能抛出错误的函数的调用。标记潜在的 suspension point 特别重要，因为 <em>suspension 会中断原子性</em>。例如，如果一个异步函数在一个给定的上下文中运行，而这个上下文是由一个串行队列保护的，那么达到一个suspension point 就意味着其他代码可以在同一个串行队列上插入其它代码。一个经典但有点老套的例子，这种原子性很重要，那就是对银行账户进行建模：如果一笔存款被记入一个账户，但在处理匹配的取款之前，操作 suspend，就会产生一个窗口期，在这个窗口期中，这些资金可以被重复使用。对于很多 Swift 程序员来说，一个更贴切的例子是 UI 线程：suspension point 是 UI 可以展示给用户的点，所以程序如果构建了部分 UI，然后 suspend，就有可能呈现出一个闪烁的、构建了一半的 UI。(请注意，在使用显式回调的代码中，suspension point也是显式调用的：suspend 会发生在外部函数返回和回调开始运行的点之间)。要求对所有潜在的 suspension point 进行标记，可以让程序员安全地假设没有潜在 suspension point 的地方将表现为原子模式，以及更容易识别有问题的非原子模式。</p><!--Because potential suspension points can only appear at points explicitly marked within an asynchronous function, long computations can still block threads.  This might happen when calling a synchronous function that just does a lot of work, or when encountering a particularly intense computational loop written directly in an asynchronous function.  In either case, the thread cannot interleave code while these computations are running, which is usually the right choice for correctness, but can also become a scalability problem.  Asynchronous programs that need to do intense computation should generally run it in a separate context.  When that’s not feasible, there will be library facilities to artificially suspend and allow other operations to be interleaved.--><p>因为潜在的 suspension point 只能出现在异步函数中明确标记的位置，所以长时间的计算仍然会阻塞线程。这种情况可能发生在调用一个只是做了很多工作的同步函数时，或者遇到直接写在异步函数中的特别密集的计算循环时。无论是哪种情况，在这些计算运行的时候，线程都不能在中间插入其它运算逻辑，这通常是正确性的正确选择，但也可能成为一个扩展性问题。需要进行高强度计算的异步程序一般应该在一个单独的上下文中运行。当这不可行时，会有第三方库的设施人为地 suspend 并允许其他操作插入执行。</p><!--Asynchronous functions should avoid calling functions that can actually block the thread, especially if they can block it waiting for work that’s not guaranteed to be currently running.  For example, acquiring a mutex can only block until some currently-running thread gives up the mutex; this is sometimes acceptable but must be used carefully to avoid introducing deadlocks or artificial scalability problems.  In contrast, waiting on a condition variable can block until some arbitrary other work gets scheduled that signals the variable; this pattern goes strongly against recommendation.--><p>异步函数应该避免调用实际上可以阻塞线程的函数，特别是当它们可以阻塞线程，等待那些不能保证当前正在运行的工作时。例如，获取一个互斥锁时只能阻塞，直到某个当前正在运行的线程放弃互斥锁；这有时是可以接受的，但必须谨慎使用，以避免引入死锁或人为的可扩展性问题。相反，在一个条件变量上的等待可以阻塞，直到一些任意的其他工作得到安排，给变量发出信号；这种模式与建议强烈相悖。</p><h2 id="设计细节"><a href="#设计细节" class="headerlink" title="设计细节"></a>设计细节</h2><h3 id="异步函数"><a href="#异步函数" class="headerlink" title="异步函数"></a>异步函数</h3><!--Function types can be marked explicitly as `async`, indicating that the function is asynchronous:--><p>函数类型可以明确标记为 <code>async</code>，表示该函数是异步的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">collect</span>(<span class="params">function</span>: () <span class="keyword">async</span> -&gt; <span class="type">Int</span>) &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--A function or initializer declaration can also be declared explicitly as `async`:--><p>一个函数或初始化器声明也可以显式声明为 <code>async</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Teacher</span> &#123;</span><br><span class="line">  <span class="keyword">init</span>(<span class="params">hiringFrom</span>: <span class="type">College</span>) <span class="keyword">async</span> <span class="keyword">throws</span> &#123;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">func</span> <span class="title function_">raiseHand</span>() <span class="keyword">async</span> -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--> **Rationale**: The `async` follows the parameter list because it is part of the function's type as well as its declaration. This follows the precedent of `throws`.--><blockquote><p><strong>理由</strong>：<code>async</code> 跟在参数列表后面，因为它既是函数类型的一部分，也是函数声明的一部分。这遵循了 <code>throws</code> 的先例。</p></blockquote><!--The type of a reference to a function or initializer declared `async` is an `async` function type. If the reference is a “curried” static reference to an instance method, it is the "inner" function type that is `async`, consistent with the usual rules for such references.--><p>声明为 <code>async</code> 的函数或构造器的引用，类型是 <code>async</code> 函数类型。如果该引用是对实例方法的 “curried” 静态引用，则按照此类引用的通常规则，”内部”函数类型为<code>async</code>。</p><!--Special functions like `deinit` and storage accessors (i.e., the getters and setters for properties and subscripts) cannot be `async`.--><p>像 <code>deinit</code> 和存储访问器这样的特殊函数（例如，属性的 getter&#x2F;setter 和 subscript）不能为 <code>async</code>。</p><!--> **Rationale**: Properties and subscripts that only have a getter could potentially be `async`. However, properties and subscripts that also have an `async` setter imply the ability to pass the reference as `inout` and drill down into the properties of that property itself, which depends on the setter effectively being an "instantaneous" (synchronous, non-throwing) operation. Prohibiting `async` properties is a simpler rule than only allowing get-only `async` properties and subscripts.--><blockquote><p><strong>理由</strong>：只有 getter 的属性和 subscript 有可能成为 <code>async</code> 的。然而，<code>async</code> setter 的属性和下标意味着能够将引用作为 <code>inout</code> 传递，并深入到该属性本身的属性，这取决于 setter 实际上是否为一个”瞬时”（同步、non-throwing）操作。比起只允许 <code>async</code> get-only 的属性和 subscript，禁止 <code>async</code> 属性是更简单的规则。</p></blockquote><blockquote><p>译者注：让 getter 和 subscript 能够标记为 async 的功能，已经包含在 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0310-effectful-readonly-properties.md">SE-0310 Effectful Read-only Properties</a> 提案里，并且已经通过了 review，在 Swift 5.5 完成了实现。</p></blockquote><!--If a function is both `async` and `throws`, then the `async` keyword must precede `throws` in the type declaration. This same rule applies if `async` and `rethrows`.--><p>如果一个函数既是 <code>async</code> 又是 <code>throws</code> 的，那么在类型声明中，<code>async</code> 关键字就必须放在 <code>throws</code> 前面。这个规则同样适用于 <code>async</code> 和 <code>rethrows</code>。</p><!--> **Rationale** : This order restriction is arbitrary, but it's not harmful, and it eliminates the potential for stylistic debates.--><blockquote><p><strong>理由</strong>：这个顺序限制是随性的，但并不会带来害处，而且以后也不需要去争辩该用哪种代码风格。</p></blockquote><!--An `async` initializer of a class that has a superclass but lacks a call to a superclass initializer will get an implicit call to `super.init()` only if the superclass has a zero-argument, synchronous, designated initializer.--><p>一个有父类但没有调用父类构造器的 <code>async</code> 构造器，只有当超类有一个零参数的、同步的、指定的初始化器时，才会得到对<code>super.init()</code>的隐式调用。</p><!--> **Rationale**: If the superclass initializer is `async`, the call to the asynchronous initializer is a potential suspension point and therefore the call (and required `await`) must be visible in the source.--><blockquote><p><strong>理由</strong>：如果超类初始化器是 <code>async</code>，对异步构造器的调用就是一个潜在的 suspension point，因此，调用(和所需的 <code>await</code>)必须在代码里可见。</p></blockquote><h3 id="异步函数类型"><a href="#异步函数类型" class="headerlink" title="异步函数类型"></a>异步函数类型</h3><!--Asynchronous function types are distinct from their synchronous counterparts. However, there is an implicit conversion from a synchronous function type to its corresponding asynchronous function type. This is similar to the implicit conversion from a non-throwing function to its throwing counterpart, which can also compose with the asynchronous function conversion. For example:--><p>异步函数的类型明显与同步函数的类型不同。然而，从同步函数的类型到其对应的异步函数的类型会有一个自动隐式转换。这类似于从一个 non-throwing 函数到其 throwing 对应函数的隐式转换，也可以与异步函数转换组合到一起。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">FunctionTypes</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> syncNonThrowing: () -&gt; <span class="type">Void</span></span><br><span class="line">  <span class="keyword">var</span> syncThrowing: () <span class="keyword">throws</span> -&gt; <span class="type">Void</span></span><br><span class="line">  <span class="keyword">var</span> asyncNonThrowing: () <span class="keyword">async</span> -&gt; <span class="type">Void</span></span><br><span class="line">  <span class="keyword">var</span> asyncThrowing: () <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Void</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">demonstrateConversions</span>() &#123;</span><br><span class="line">    <span class="comment">// Okay to add &#x27;async&#x27; and/or &#x27;throws&#x27;    </span></span><br><span class="line">    asyncNonThrowing <span class="operator">=</span> syncNonThrowing</span><br><span class="line">    asyncThrowing <span class="operator">=</span> syncThrowing</span><br><span class="line">    syncThrowing <span class="operator">=</span> syncNonThrowing</span><br><span class="line">    asyncThrowing <span class="operator">=</span> asyncNonThrowing</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Error to remove &#x27;async&#x27; or &#x27;throws&#x27;</span></span><br><span class="line">    syncNonThrowing <span class="operator">=</span> asyncNonThrowing <span class="comment">// error</span></span><br><span class="line">    syncThrowing <span class="operator">=</span> asyncThrowing       <span class="comment">// error</span></span><br><span class="line">    syncNonThrowing <span class="operator">=</span> syncThrowing     <span class="comment">// error</span></span><br><span class="line">    asyncNonThrowing <span class="operator">=</span> syncThrowing    <span class="comment">// error</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="await-表达式"><a href="#await-表达式" class="headerlink" title="await 表达式"></a>await 表达式</h3><!--A call to a value of `async` function type (including a direct call to an `async` function) introduces a potential suspension point. Any potential suspension point must occur within an asynchronous context (e.g., an `async` function). Furthermore, it must occur within the operand of an `await` expression. --><p>对 <code>async</code> 函数的调用（包括对 <code>async</code> 函数的直接调用）会引入一个潜在的 suspension point。任何潜在的 suspension point 必须发生在异步上下文中（例如，一个 <code>async</code> 函数）。此外，它必须发生在 <code>await</code> 表达式的对象内。</p><p>请看下面的例子：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// func redirectURL(for url: URL) async -&gt; URL &#123; ... &#125;</span></span><br><span class="line"><span class="comment">// func dataTask(with: URL) async throws -&gt; (Data, URLResponse) &#123; ... &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> newURL <span class="operator">=</span> <span class="keyword">await</span> server.redirectURL(for: url)</span><br><span class="line"><span class="keyword">let</span> (data, response) <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> session.dataTask(with: newURL)</span><br></pre></td></tr></table></figure><!--In this code example, a task suspension may happen during the calls to `redirectURL(for:)` and `dataTask(with:)` because they are async functions. Thus, both call expressions must be contained within some `await` expression, because each call contains a potential suspension point. An `await` operand may contain more than one potential suspension point. For example, we can use one `await` to cover both potential suspension points from our example by rewriting it as:--><p>在这个代码示例中，在调用 <code>redirectURL(for:)</code> 和 <code>dataTask(with:)</code> 期间可能会发生任务suspension，因为它们是异步函数。因此，这两个调用表达式必须包含在某个 <code>await</code> 表达式中，因为每个调用都包含一个潜在的 suspension point。一个 <code>await</code> 表达式可以包含一个以上的潜在 suspension point。例如，我们可以使用一个 <code>await</code> 来覆盖上面例子中的两个潜在的 suspension point，将其改写为：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> (data, response) <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> session.dataTask(with: server.redirectURL(for: url))</span><br></pre></td></tr></table></figure><!--The `await` has no additional semantics; like `try`, it merely marks that an asynchronous call is being made.  The type of the `await` expression is the type of its operand, and its result is the result of its operand. An `await` operand may also have no potential suspension points, which will result in a warning from the Swift compiler, following the precedent of `try` expressions:--><p><code>await</code> 没有额外的语义；与 <code>try</code> 一样，它只是标志着一个异步调用正在进行。<code>await</code> 表达式的类型是其操作对象的类型，其结果是其操作对象的结果。一个 <code>await</code> 操作数也可能没有潜在的 suspension point，这将导致 Swift 编译器发出警告，这是跟随 <code>try</code> 表达式的先例：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> x <span class="operator">=</span> <span class="keyword">await</span> synchronous() <span class="comment">// warning: no calls to &#x27;async&#x27; functions occur within &#x27;await&#x27; expression</span></span><br></pre></td></tr></table></figure><!--> **Rationale**: It is important that asynchronous calls are clearly identifiable within the function because they may introduce suspension points, which break the atomicity of the operation.  The suspension points may be inherent to the call (because the asynchronous call must execute on a different executor) or simply be part of the implementation of the callee, but in either case it is semantically important and the programmer needs to acknowledge it. `await` expressions are also an indicator of asynchronous code, which interacts with inference in closures; see the section on [Closures](#closures) for more information.--><blockquote><p><strong>理由</strong>：重要的是，异步调用在函数中应该具有明确的标识，因为它们可能会引入 suspension point，从而打破操作的原子性。suspension point 可能是调用所固有的（因为异步调用必须在不同的 executor 上执行），或者仅仅是被调用者实现的一部分，但无论哪种情况，它在语义上都是很重要的，工程师都需要意识到它。<code>await</code> 表达式也是异步代码的一个标识，它与闭包中的推导交互；更多信息请参见 <a href="#closures">Closure</a> 一节。</p></blockquote><!--A potential suspension point must not occur within an autoclosure that is not of `async` function type.--><p>潜在的 suspension point 不可以发生在非 <code>async</code> 函数的 <code>@autoclosure</code> 里。</p><!--A potential suspension point must not occur within a `defer` block.--><p>潜在的 suspension point 不可以发生在 <code>defer</code> 里。</p><!--If both `await` and a variant of `try` (including `try!` and `try?`) are applied to the same subexpression, `await` must follow the `try`/`try!`/`try?`:--><p>如果 <code>await</code> 和 <code>try</code> 的变体（包括 <code>try!</code> 和 <code>try?</code>）被应用于同一个子表达式，<code>await</code> 必须跟在 <code>try</code>&#x2F;<code>try!</code>&#x2F;<code>try?</code> 后面。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> (data, response) <span class="operator">=</span> <span class="keyword">await</span> <span class="keyword">try</span> session.dataTask(with: server.redirectURL(for: url)) <span class="comment">// error: must be `try await`</span></span><br><span class="line"><span class="keyword">let</span> (data, response) <span class="operator">=</span> <span class="keyword">await</span> (<span class="keyword">try</span> session.dataTask(with: server.redirectURL(for: url))) <span class="comment">// okay due to parentheses</span></span><br></pre></td></tr></table></figure><!--> **Rationale**: this restriction is arbitrary, but follows the equally-arbitrary restriction on the ordering of `async throws` in preventing stylistic debates.--><blockquote><p><strong>理由</strong>：这一限制也是任意的，但延续了对 <code>async throws</code> 顺序的限制，以防止代码风格上的争论。</p></blockquote><h3 id="Closures"><a href="#Closures" class="headerlink" title="Closures"></a>Closures</h3><!--A closure can have `async` function type. Such closures can be explicitly marked as `async` as follows:--><p>一个闭包也可以是 <code>async</code> 类型的。这类闭包可以明确标记为 <code>async</code> ，就像这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">&#123; () <span class="keyword">async</span> -&gt; <span class="type">Int</span> <span class="keyword">in</span></span><br><span class="line">  <span class="built_in">print</span>(<span class="string">&quot;here&quot;</span>)</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">await</span> getInt()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--An anonymous closure is inferred to have `async` function type if it contains an `await` expression.--><p>一个匿名闭包如果包含一个 <code>await</code> 表达式，就会被推导为 <code>async</code> 类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> closure <span class="operator">=</span> &#123; <span class="keyword">await</span> getInt() &#125; <span class="comment">// implicitly async</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> closure2 <span class="operator">=</span> &#123; () -&gt; <span class="type">Int</span> <span class="keyword">in</span>     <span class="comment">// implicitly async</span></span><br><span class="line">  <span class="built_in">print</span>(<span class="string">&quot;here&quot;</span>)</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">await</span> getInt()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Note that inference of `async` on a closure does not propagate to its enclosing or nested functions or closures, because those contexts are separably asynchronous or synchronous. For example, only `closure6` is inferred to be `async` in this situation:--><p>请注意，闭包的 <code>async</code> 推导不会影响到它外层或嵌套的函数或闭包，因为这些上下文是独立的，异步或同步的。例如，在这种情况下，只有 <code>closure6</code> 被推导为 <code>async</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// func getInt() async -&gt; Int &#123; ... &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> closure5 <span class="operator">=</span> &#123; () -&gt; <span class="type">Int</span> <span class="keyword">in</span>       <span class="comment">// not &#x27;async&#x27;</span></span><br><span class="line">  <span class="keyword">let</span> closure6 <span class="operator">=</span> &#123; () -&gt; <span class="type">Int</span> <span class="keyword">in</span>     <span class="comment">// implicitly async</span></span><br><span class="line">    <span class="keyword">if</span> randomBool() &#123;</span><br><span class="line">      <span class="built_in">print</span>(<span class="string">&quot;there&quot;</span>)</span><br><span class="line">      <span class="keyword">return</span> <span class="keyword">await</span> getInt()</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">let</span> closure7 <span class="operator">=</span> &#123; () -&gt; <span class="type">Int</span> <span class="keyword">in</span> <span class="number">7</span> &#125;  <span class="comment">// not &#x27;async&#x27;</span></span><br><span class="line">      <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="built_in">print</span>(<span class="string">&quot;here&quot;</span>)</span><br><span class="line">  <span class="keyword">return</span> <span class="number">5</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="重载和重载规则"><a href="#重载和重载规则" class="headerlink" title="重载和重载规则"></a>重载和重载规则</h3><!--Existing Swift APIs generally support asynchronous functions via a callback interface, e.g.,--><p>现有的 Swift API 一般通过回调接口支持异步函数，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">doSomething</span>(<span class="params">completionHandler</span>: ((<span class="type">String</span>) -&gt; <span class="type">Void</span>)<span class="operator">?</span> <span class="operator">=</span> <span class="literal">nil</span>) &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--Many such APIs are likely to be updated by adding an `async` form:--><p>许多这样的 API 很可能会增加一个 <code>async</code> 形式的函数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">doSomething</span>() <span class="keyword">async</span> -&gt; <span class="type">String</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--These two functions have different names and signatures, even though they share the same base name. However, either of them can be called with no parameters (due to the defaulted completion handler), which would present a problem for existing code:--><p>这两个函数的名称和签名是不同的，尽管它们的名字一样。然而，它们中的任何一个函数都可能会在没有参数的情况下被调用（由于默认的代码补齐），这将给现有的代码带来一个问题：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">doSomething() <span class="comment">// problem: can call either, unmodified Swift rules prefer the `async` version</span></span><br></pre></td></tr></table></figure><!--Swift's overloading rules prefer to call a function with fewer default arguments, so the addition of the `async` function would break existing code that called the original `doSomething(completionHandler:)` with no completion handler. This would get an error along the lines of:--><p>Swift 的重载规则更倾向于调用缺省参数较少的函数，所以增加 <code>async</code> 函数会破坏现有的代码，这些代码调用了原来的 <code>doSomething(completionHandler:)</code>，只是没传入 <code>completionHandler</code>。这将得到一个类似这样的编译错误：</p><figure class="highlight subunit"><table><tr><td class="code"><pre><span class="line"><span class="keyword">error: </span>`async` function cannot be called from non-asynchronous context</span><br></pre></td></tr></table></figure><!--This presents problems for code evolution, because developers of existing asynchronous libraries would have to either have a hard compatiblity break (e.g, to a new major version) or would need have different names for all of the new `async` versions. The latter would likely result in a scheme such as [C#'s pervasive `Async` suffix](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model).--><p>这会给代码演进带来了问题，因为现有异步库的作者要么硬性打破兼容性（例如，一个新的大版本），要么需要为所有新的 <code>async</code> 版本取不同的名字。后者很可能会演变类似于 <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model">C# 普遍的 <code>Async</code> 后缀</a>。</p><!--Instead, we propose an overload-resolution rule to select the appropriate function based on the context of the call. Given a call, overload resolution prefers non-`async` functions within a synchronous context (because such contexts cannot contain a call to an `async` function).  Furthermore, overload resolution prefers `async` functions within an asynchronous context (because such contexts should avoid stepping out of the asynchronous model into blocking APIs). When overload resolution selects an `async` function, that call is still subject to the rule that it must occur within an `await` expression.--><p>相反，我们提出了一个重载解决规则，根据调用的上下文选择适当的函数。给定一个调用，在同步的上下文中，重载解析会优先选择非 <code>async</code> 函数（因为这种上下文不能包含对 <code>async</code> 函数的调用）。此外，在异步上下文中，重载解析会优先选择 <code>async</code> 函数（因为这种上下文应该避免从异步模型中跳出而进入阻塞的 API）。当重载解析选择 <code>async</code> 函数时，该调用仍然需要加上 <code>await</code>。</p><!--Note that we follow the design of `throws` in disallowing overloads that differ *only* in `async`:--><p>需要注意的是，我们延续了 <code>throws</code> 的设计，不允许只有 <code>async</code> 不同的重载：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">doSomething</span>() -&gt; <span class="type">String</span> &#123; <span class="comment">/* ... */</span> &#125;       <span class="comment">// synchronous, blocking</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">doSomething</span>() <span class="keyword">async</span> -&gt; <span class="type">String</span> &#123; <span class="comment">/* ... */</span> &#125; <span class="comment">// asynchronous</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// error: redeclaration of function `doSomething()`.</span></span><br></pre></td></tr></table></figure><h3 id="autoclosure"><a href="#autoclosure" class="headerlink" title="autoclosure"></a>autoclosure</h3><!--A function may not take an autoclosure parameter of `async` function type ufnless the function itself is `async`. For example, the following declaration is ill-formed:--><p>除非函数本身是 <code>async</code> 函数类型，否则函数不能接受 <code>async</code> 函数类型的 <code>@autoclosure</code> 参数。例如，下面的声明就是不规范的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// error: async autoclosure in a function that is not itself &#x27;async&#x27;</span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">computeArgumentLater</span>&lt;<span class="type">T</span>&gt;(<span class="keyword">_</span> <span class="params">fn</span>: <span class="keyword">@escaping</span> <span class="keyword">@autoclosure</span> () <span class="keyword">async</span> -&gt; <span class="type">T</span>) &#123; &#125; </span><br></pre></td></tr></table></figure><!--This restriction exists for several reasons. Consider the following example:--><p>这种限制的存在有几个原因。请看下面的例子：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// func getIntSlowly() async -&gt; Int &#123; ... &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> closure <span class="operator">=</span> &#123;</span><br><span class="line">  computeArgumentLater(<span class="keyword">await</span> getIntSlowly())</span><br><span class="line">  <span class="built_in">print</span>(<span class="string">&quot;hello&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--At first glance, the `await` expression implies to the programmer that there is a potential suspension point *prior* to the call to `computeArgumentLater(_:)`, which is not actually the case: the potential suspension point is *within* the (auto)closure that is passed and used within the body of `computeArgumentLater(_:)`. This causes a few problems. First, the fact that `await` appears to be prior to the call means that `closure` would be inferred to have `async` function type, which is also incorrect: all of the code in `closure` is synchronous. Second, because an `await`'s operand only needs to contain a potential suspension point somewhere within it, an equivalent rewriting of the call should be:--><p>乍一看，这里的 <code>await</code> 表达式在向工程师暗示，调用 <code>computeArgumentLater(_:)</code> 之前有一个潜在的 suspension point，但实际情况并非如此：潜在的 suspension point 是在 <code>computeArgumentLater(_:)</code> 主体传递和使用的闭包中。这导致了一些问题，首先 <code>await</code> 出现在调用之前意味着 <code>closure</code> 会被推导为具有 <code>async</code> 函数类型，这也是不正确的：<code>closure</code> 中的所有代码都是同步的。其次，由于 <code>await</code> 的操作对象只需要在其中某个地方包含一个潜在的 suspension point，因此调用的等效代码应该是：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">await</span> computeArgumentLater(getIntSlowly())</span><br></pre></td></tr></table></figure><!--But, because the argument is an autoclosure, this rewriting is no longer semantics-preserving. Thus, the restriction on `async` autoclosure parameters avoids these problems by ensuring that `async` autoclosure parameters can only be used in asynchronous contexts.--><p>但是，由于参数是 autoclosure 的，这种重写不再保留之前的语义。因此，对 <code>async</code> autoclosure 参数的限制可以避免这些问题，只要确保 <code>async</code> autoclosure 参数只能在异步上下文中使用。</p><h3 id="Protocol-conformance"><a href="#Protocol-conformance" class="headerlink" title="Protocol conformance"></a>Protocol conformance</h3><!--A protocol requirement can be declared as `async`. Such a requirement can be satisfied by an `async` or synchronous function. However, a synchronous function requirement cannot be satisfied by an `async` function. For example:--><p>协议要求可声明为 <code>async</code>。这种要求可由 <code>async</code> 或同步函数来满足。但是，同步函数的要求不能由<code>async</code> 函数来满足。例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Asynchronous</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">f</span>() <span class="keyword">async</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protocol</span> <span class="title class_">Synchronous</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">g</span>()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">S1</span>: <span class="title class_ inherited__">Asynchronous</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">f</span>() <span class="keyword">async</span> &#123; &#125; <span class="comment">// okay, exactly matches</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">S2</span>: <span class="title class_ inherited__">Asynchronous</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">f</span>() &#123; &#125; <span class="comment">// okay, synchronous function satisfying async requirement</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">S3</span>: <span class="title class_ inherited__">Synchronous</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">g</span>() &#123; &#125; <span class="comment">// okay, exactly matches</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">S4</span>: <span class="title class_ inherited__">Synchronous</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">g</span>() <span class="keyword">async</span> &#123; &#125; <span class="comment">// error: cannot satisfy synchronous requirement with an async function</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This behavior follows the subtyping/implicit conversion rule for asynchronous functions, as is precedented by the behavior of `throws`.--><p>这种行为遵循了异步函数的子类型&#x2F;隐式转换规则，正如 <code>throws</code> 以前的行为。</p><h2 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a>代码兼容性</h2><!--This proposal is generally additive: existing code does not use any of the new features (e.g., does not create `async` functions or closures) and will not be impacted. However, it introduces two new contextual keywords, `async` and `await`.--><p>这个提案总体上是补充性的：现有代码不会使用任何新的功能（例如，不会创建 <code>async</code> 函数或闭包），不会受到影响。但是，它引入了两个新的上下文关键字 <code>async</code> 和 <code>await</code>。</p><!--The positions of the new uses of `async` within the grammar (function declarations and function types) allows us to treat `async` as a contextual keyword without breaking source compatibility. A user-defined `async` cannot occur in those grammatical positions in well-formed code.--><p><code>async</code> 在语法中的位置（函数声明和函数类型）使我们能够在不破坏源码兼容性的情况下将 <code>async</code> 作为上下文关键字来处理。在格式良好的代码中，用户定义的 <code>async</code> 不能出现在这些语法位置上。</p><!--The `await` contextual keyword is more problematic, because it occurs within an expression. For example, one could define a function `await` in Swift today:--><p><code>await</code> 上下文关键字比较麻烦，因为它发生在一个表达式中。例如，今天可以在 Swift 中定义一个函数 <code>await</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">await</span>(<span class="keyword">_</span> <span class="params">x</span>: <span class="type">Int</span>, <span class="keyword">_</span> <span class="params">y</span>: <span class="type">Int</span>) -&gt; <span class="type">Int</span> &#123; x <span class="operator">+</span> y &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> result <span class="operator">=</span> <span class="keyword">await</span>(<span class="number">1</span>, <span class="number">2</span>)</span><br></pre></td></tr></table></figure><!--This is well-formed code today that is a call to the `await` function. With this proposal, this code becomes an `await` expression with the subexpression `(1, 2)`. This will manifest as a compile-time error for existing Swift programs, because `await` can only be used within an asynchronous context, and no existing Swift programs have such a context. Such functions do not appear to be common, so we believe this is an acceptable source break as part of the introduction of async/await.--><p>目前这段代码格式没有任何问题，它是对 <code>await</code> 函数的调用。但在这个提案里，这段代码变成了一个带有子表达式 <code>(1, 2)</code> 的 <code>await</code> 表达式。这对于现有的 Swift 程序来说，将表现为编译时错误，因为 <code>await</code> 只能在异步上下文中使用，而现有的 Swift 程序都没有这样的上下文。这样的函数似乎并不常见，所以我们认为这是一个可以接受的 source break，作为引入 async&#x2F;await 的一部分。</p><h2 id="对于-ABI-的影响"><a href="#对于-ABI-的影响" class="headerlink" title="对于 ABI 的影响"></a>对于 ABI 的影响</h2><!--Asynchronous functions and function types are additive to the ABI, so there is no effect on ABI stability, because existing (synchronous) functions and function types are unchanged.--><p>异步函数和函数类型对 ABI 是补充性的，所以对 ABI 的稳定性没有影响，因为现有的（同步）函数和函数类型是不变的。</p><h2 id="对于-API-的影响"><a href="#对于-API-的影响" class="headerlink" title="对于 API 的影响"></a>对于 API 的影响</h2><!--The ABI for an `async` function is completely different from the ABI for a synchronous function (e.g., they have incompatible calling conventions), so the addition or removal of `async` from a function or type is not a resilient change.--><p><code>async</code> 函数的 ABI 与同步函数的 ABI 完全不同（例如，它们有不兼容的调用惯例），所以从函数或类型中添加或删除 <code>async</code> 并不是一个兼容的修改。</p><h2 id="未来方向"><a href="#未来方向" class="headerlink" title="未来方向"></a>未来方向</h2><h3 id="reasync"><a href="#reasync" class="headerlink" title="reasync"></a><code>reasync</code></h3><!--Swift's `rethrows` is a mechanism for indicating that a particular function is throwing only when one of the arguments passed to it is a function that itself throws. For example, `Sequence.map` makes use of `rethrows` because the only way the operation can throw is if the transform itself throws:--><p>Swift 的 <code>rethrows</code> 是一种机制，用于表明只有当传递给它的参数之一是一个本身就会 <code>throws</code> 的函数时，某个函数才会抛出。例如，<code>Sequence.map</code> 就使用了 <code>rethrows</code>，因为只有当 <code>transform</code> 本身是 <code>throws</code> 时，<code>map</code> 操作才会 <code>throws</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Sequence</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">map</span>&lt;<span class="type">Transformed</span>&gt;(<span class="params">transform</span>: (<span class="type">Element</span>) <span class="keyword">throws</span> -&gt; <span class="type">Transformed</span>) <span class="keyword">rethrows</span> -&gt; [<span class="type">Transformed</span>] &#123;</span><br><span class="line">    <span class="keyword">var</span> result <span class="operator">=</span> [<span class="type">Transformed</span>]()</span><br><span class="line">    <span class="keyword">var</span> iterator <span class="operator">=</span> <span class="keyword">self</span>.makeIterator()</span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">let</span> element <span class="operator">=</span> iterator.next() &#123;</span><br><span class="line">      result.append(<span class="keyword">try</span> transform(element))   <span class="comment">// note: this is the only `try`!</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Here are uses of `map` in practice:--><p>这是实际代码中 <code>map</code> 的使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">_</span> <span class="operator">=</span> [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].map &#123; <span class="type">String</span>(<span class="variable">$0</span>) &#125;  <span class="comment">// okay: map does not throw because the closure does not throw</span></span><br><span class="line"><span class="keyword">_</span> <span class="operator">=</span> <span class="keyword">try</span> [<span class="string">&quot;1&quot;</span>, <span class="string">&quot;2&quot;</span>, <span class="string">&quot;3&quot;</span>].map &#123; (string: <span class="type">String</span>) -&gt; <span class="type">Int</span> <span class="keyword">in</span></span><br><span class="line">  <span class="keyword">guard</span> <span class="keyword">let</span> result <span class="operator">=</span> <span class="type">Int</span>(string) <span class="keyword">else</span> &#123; <span class="keyword">throw</span> <span class="type">IntParseError</span>(string) &#125;</span><br><span class="line">  <span class="keyword">return</span> result</span><br><span class="line">&#125; <span class="comment">// okay: map can throw because the closure can throw</span></span><br></pre></td></tr></table></figure><!--The same notion could be applied to `async` functions. For example, we could imagine making `map` asynchronous when its argument is asynchronous with `reasync`:--><p>同样的概念可以应用于 <code>async</code> 函数。例如，我们可以想象当 <code>map</code> 传入的闭包是异步的函数时，<code>map</code> 也将成为异步函数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Sequence</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">map</span>&lt;<span class="type">Transformed</span>&gt;(<span class="params">transform</span>: (<span class="type">Element</span>) <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">Transformed</span>) reasync <span class="keyword">rethrows</span> -&gt; [<span class="type">Transformed</span>] &#123;</span><br><span class="line">    <span class="keyword">var</span> result <span class="operator">=</span> [<span class="type">Transformed</span>]()</span><br><span class="line">    <span class="keyword">var</span> iterator <span class="operator">=</span> <span class="keyword">self</span>.makeIterator()</span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">let</span> element <span class="operator">=</span> iterator.next() &#123;</span><br><span class="line">      result.append(<span class="keyword">try</span> <span class="keyword">await</span> transform(element))   <span class="comment">// note: this is the only `try` and only `await`!</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--*Conceptually*, this is fine: when provided with an `async` function, `map` will be treated as `async` (and you'll need to `await` the result), whereas providing it with a non-`async` function, `map` will be treated as synchronous (and won't require `await`).--><p><em>理论上</em>，这是没问题的：当提供一个 <code>async</code> 函数时，<code>map</code> 将被视为 <code>async</code>（你需要 <code>await</code> 结果），而提供一个非 <code>async</code> 函数时，<code>map</code> 将被视为同步的（不需要 <code>await</code>)。</p><!--*In practice*, there are a few problems here:--><p><em>在实践中</em>，这里会有几个问题：</p><!--* This is probably not a very good implementation of an asynchronous `map` on a sequence. More likely, we would want a concurrent implementation that (say) processes up to number-of-cores elements concurrently.* The ABI of throwing functions is intentionally designed to make it possible for a `rethrows` function to act as a non-throwing function, so a single ABI entry point suffices for both throwing and non-throwing calls. The same is not true of `async` functions, which have a radically different ABI that is necessarily less efficient than the ABI for synchronous functions.--><ul><li>对于 Sequence 来说这可能不是一个很好的 <code>map</code> 实现。更有可能的是，我们想要一个并发的实现，（比如）并发处理多个元素。</li><li><code>throws</code> 函数的 ABI 被有意设计为使 <code>rethrows</code> 函数可以作为一个非抛出函数，因此一个 ABI 入口点就足以满足 throws 和 non-throws 的调用。而 <code>async</code> 函数则不同，它的 ABI 完全不同，其效率必然低于同步函数的 ABI。</li></ul><!--For something like `Sequence.map` that might become concurrent, `reasync` is likely the wrong tool: overloading for `async` closures to provide a separate (concurrent) implementation is likely the better answer. So, `reasync` is likely to be much less generally applicable than `rethrows`.--><p>对于像 <code>Sequence.map</code> 这种可能并发的东西，<code>reasync</code> 可能是错误的工具：为 <code>async</code> 闭包重载以提供一个单独的（并发）实现可能是更好的答案。因此，<code>reasync</code> 可能没有 <code>rethrows</code> 那么普适。</p><!--There are undoubtedly some uses for `reasync`, such as the `??` operator for optionals, where the `async` implementation degrades nicely to a synchronous implementation:--><p>毋庸置疑，<code>reasync</code> 有它的用途，比如 <code>Optional</code> 的 <code>??</code> 操作符，<code>async</code> 的实现可以很好地降级为同步实现：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">??&lt;</span><span class="type">T</span><span class="operator">&gt;</span>(</span><br><span class="line">    <span class="keyword">_</span> optValue: <span class="type">T</span>?, <span class="keyword">_</span> defaultValue: <span class="keyword">@autoclosure</span> () <span class="keyword">async</span> <span class="keyword">throws</span> -&gt; <span class="type">T</span></span><br><span class="line">) reasync <span class="keyword">rethrows</span> -&gt; <span class="type">T</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">let</span> value <span class="operator">=</span> optValue &#123;</span><br><span class="line">    <span class="keyword">return</span> value</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">try</span> <span class="keyword">await</span> defaultValue()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--For such cases, the ABI concern described above can likely be addressed by emitting two entrypoints: one when the argument is `async` and one when it is not. However, the implementation is complex enough that the authors are not yet ready to commit to this design.--><p>对于这种情况，可以通过发出两个入口点来解决上文所述的 ABI 问题：一个是当参数是 <code>async</code> 时，另一个是当参数不是时。然而，由于实现方式非常复杂，作者还没有准备好采用这种设计。</p><h2 id="替代方案"><a href="#替代方案" class="headerlink" title="替代方案"></a>替代方案</h2><h3 id="让-await-隐含-try"><a href="#让-await-隐含-try" class="headerlink" title="让 await 隐含 try"></a>让 <code>await</code> 隐含 <code>try</code></h3><!--Many asynchronous APIs involve file I/O, networking, or other failable operations, and therefore will be both `async` and `throws`. At the call site, this means `try await` will be repeated many times. To reduce the boilerplate, `await` could imply `try`, so the following two lines would be equivalent:--><p>许多异步 API 都涉及文件 I&#x2F;O、网络请求或其它可能失败的操作，因此会同时出现 <code>async</code> 和 <code>throws</code>。在调用方，这意味着 <code>try await</code> 将被重复多次。为了减少模板，可以让 <code>await</code> 隐含 <code>try</code>，所以下面两行将是等价的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> dataResource  <span class="operator">=</span> <span class="keyword">await</span> loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>)</span><br><span class="line"><span class="keyword">let</span> dataResource  <span class="operator">=</span> <span class="keyword">try</span> <span class="keyword">await</span> loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>)</span><br></pre></td></tr></table></figure><!--We chose not to make `await` imply `try` because they are expressing different kinds of concerns: `await` is about a potential suspension point, where other code might execute in between when you make the call and it when it returns, while `try` is about control flow out of the block.--><p>我们选择不让 <code>await</code> 隐含 <code>try</code>，因为它们表达的是不同的含义。<code>await</code> 是关于一个潜在的 suspension point，即在你进行调用和它返回之间可能会有其他代码执行，而 <code>try</code> 则是关于 block 外的控制流。</p><!--One other motivation that has come up for making `await` imply `try` is related to task cancellation. If task cancellation were modeled as a thrown error, and every potential suspension point implicitly checked whether the task was cancelled, then every potential suspension point could throw: in such cases `await` might as well imply `try` because every `await` can potentially exit with an error. Task cancellation is covered in the [Structured Concurrency](https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md) proposal, and does *not* model cancellation solely as a thrown error nor does it introduce implicit cancellation checks at each potential suspension point.--><p>使 <code>await</code> 隐含 <code>try</code> 的另一个原因与任务取消有关。如果任务取消被建模为一个抛出的错误，并且每个潜在的 suspension point 都隐式地检查任务是否被取消，那么每个潜在的 suspension point 都可能抛出：在这种情况下，<code>await</code> 也可能意味着 <code>try</code>，因为每个 <code>await</code> 都可能带着错误退出。任务取消在<a href="https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md">结构化并发</a>提案中有所涉及，并<em>没有</em>仅将取消建模为抛出的错误，也没有在每个潜在的 suspension point 引入隐式的取消检查。</p><h3 id="启动-async-任务"><a href="#启动-async-任务" class="headerlink" title="启动 async 任务"></a>启动 async 任务</h3><!--Because only `async` code can call other `async` code, this proposal provides no way to initiate asynchronous code. This is intentional: all asynchronous code runs within the context of a "task", a notion which is defined in the [Structured Concurrency](https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md) proposal. That proposal provides the ability to define asynchronous entry points to the program via `@main`, e.g.,--><p>因为只有 <code>async</code> 代码才能调用其他 <code>async</code> 代码，所以本提案没有提供启动异步代码的方法。这是有意的：所有异步代码都在 “task” 的上下文中运行，这个概念在<a href="https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md">结构化并发</a>提案中得到了定义。该提案提供了通过 <code>@main</code> 来定义程序的异步入口的能力，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@main</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">MyProgram</span> &#123;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">main</span>() <span class="keyword">async</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Additionally, top-level code is not considered an asynchronous context in this proposal, so the following program is ill-formed:--><p>此外，在本提案中，顶层代码不被视为异步上下文，所以下面的程序是不合法的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">f</span>() <span class="keyword">async</span> -&gt; <span class="type">String</span> &#123; <span class="string">&quot;hello, asynchronously&quot;</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="keyword">await</span> f()) <span class="comment">// error: cannot call asynchronous function in top-level code</span></span><br></pre></td></tr></table></figure><!--This, too, will be addressed in a subsequent proposal that properly accounts for top-level variables.--><p>这一点也将在随后的提案中得到解决，该提案将适当考虑到顶层变量。</p><!--None of the concerns for top-level code affect the fundamental mechanisms of async/await as defined in this proposal.--><p>对顶层代码的处理并不会影响本提案中定义的 async&#x2F;await 基本机制。</p><h3 id="await-作为语法糖"><a href="#await-作为语法糖" class="headerlink" title="await 作为语法糖"></a>await 作为语法糖</h3><!--This proposal makes `async` functions a core part of the Swift type system, distinct from synchronous functions. An alternative design would leave the type system unchanged, and instead make `async` and `await` syntactic sugar over some `Future<T, Error>` type, e.g.,--><p>这个建议使 <code>async</code> 函数成为 Swift 类型系统的核心部分，与同步函数不同。另一种设计是不改变类型系统，而是将 <code>async</code> 和 <code>await</code> 的语法糖化在一些 <code>Future&lt;T, Error&gt;</code> 类型上，例如：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">async</span> <span class="keyword">func</span> <span class="title function_">processImageData</span>() <span class="keyword">throws</span> -&gt; <span class="type">Future</span>&lt;<span class="type">Image</span>, <span class="type">Error</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">let</span> dataResource  <span class="operator">=</span> <span class="keyword">try</span> loadWebResource(<span class="string">&quot;dataprofile.txt&quot;</span>).await()</span><br><span class="line">  <span class="keyword">let</span> imageResource <span class="operator">=</span> <span class="keyword">try</span> loadWebResource(<span class="string">&quot;imagedata.dat&quot;</span>).await()</span><br><span class="line">  <span class="keyword">let</span> imageTmp      <span class="operator">=</span> <span class="keyword">try</span> decodeImage(dataResource, imageResource).await()</span><br><span class="line">  <span class="keyword">let</span> imageResult   <span class="operator">=</span> <span class="keyword">try</span> dewarpAndCleanupImage(imageTmp).await()</span><br><span class="line">  <span class="keyword">return</span> imageResult</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This approach has a number of downsides vs. the proposed approach here:--><p>这种方法与这里提出的方法相比，有许多缺点：</p><!--* There is no universal `Future` type on which to build it in the Swift ecosystem. If the Swift ecosystem had mostly settled on a single future type already (e.g., if there were already one in the standard library), a syntactic-sugar approach like the above would codify existing practice. Lacking such a type, one would have to try to abstract over all of the different kinds of future types with some kind of `Futurable` protocol. This may be possible for some set of future types, but would give up any guarantees about the behavior or performance of asynchronous code.* It is inconsistent with the design of `throws`. The result type of asynchronous functions in this model is the future type (or "any `Futurable` type"), rather than the actual returned value. They must always be `await`'ed immediately (hence the postfix syntax) or you'll end up working with futures when you actually care about the result of the asynchronous operation. This becomes a programming-with-futures model rather than an asynchronous-programming model, when many other aspects of the `async` design intentionally push away from thinking about the futures.* Taking `async` out of the type system would eliminate the ability to do overloading based on `async`. See the prior section on the reasons for overloading on `async`.* Futures are relatively heavyweight types, and forming one for every async operation has nontrivial costs in both code size and performance. In contrast, deep integration with the type system allows `async` functions to be purpose-built and optimized for efficient suspension. All levels of the Swift compiler and runtime can optimize `async` functions in a manner that would not be possible with future-returning functions.--><ul><li>在 Swift 生态系统中，没有通用的 <code>Future</code> 类型可供使用。如果 Swift 生态系统已经基本确定了一个单一的 <code>Future</code> 类型(例如，如果标准库中已经有了一个 <code>Future</code> 类型)，那么像上面这样的语法糖方法就会改变现有的实践。如果缺乏这样的类型，就必须尝试用某种 <code>Futurable</code> 协议来抽象所有不同种类的 <code>Future</code> 类型。对于某些 <code>Future</code> 类型来说，这也许是可能的，但会放弃对异步代码行为或性能的任何保证。</li><li>这与 <code>throws</code> 的设计不一致。在这个模型中，异步函数的结果类型是 <code>Future</code> 类型（或 “任何<code>Futurable</code> 类型”），而不是实际返回值。它们必须总是立即 <code>await</code>，否则当你关心异步操作真正的结果时，你最终还是会到用 <code>Future</code> 的接口。这就变成了一个使用 <code>Future</code> 的编程模型，而不是异步编程模型，并且 <code>async</code> 设计的许多方面都有意不去考虑 <code>Future</code>。</li><li>把 <code>async</code> 从类型系统中拿出来，就会限制基于 <code>async</code> 进行重载的能力。参见前文关于在 <code>async</code> 上进行重载的原因。</li><li><code>Future</code> 是比较重量级的类型，为每一个 <code>async</code> 操作生成一个类型实例在代码大小和性能上都有不小的代价。相比之下，与类型系统的深度集成，使得 <code>async</code> 函数可以有针对性地构建和优化，从而实现高效 suspension。Swift 编译器和运行时的每一层都可以对 <code>async</code> 函数进行优化，而这种优化方式在基于 <code>Future</code> 的模型里是几乎不可行的。</li></ul><h2 id="修订历史"><a href="#修订历史" class="headerlink" title="修订历史"></a>修订历史</h2><ul><li><p>Post-review changes:</p><ul><li>Replaced <code>await try</code> with <code>try await</code>.</li><li>Added syntactic-sugar alternative design.</li></ul></li><li><p>Changes in the second pitch:</p><ul><li>One can no longer directly overload <code>async</code> and non-<code>async</code> functions. Overload resolution support remains, however, with additional justification.</li><li>Added an implicit conversion from a synchronous function to an asynchronous function.</li><li>Added <code>await try</code> ordering restriction to match the <code>async throws</code> restriction.</li><li>Added support for <code>async</code> initializers.</li><li>Added support for synchronous functions satisfying an <code>async</code> protocol requirement.</li><li>Added discussion of <code>reasync</code>.</li><li>Added justification for <code>await</code> not implying <code>try</code>.</li><li>Added justification for <code>async</code> following the function parameter list.</li></ul></li><li><p>Original pitch (<a href="https://github.com/DougGregor/swift-evolution/blob/092c05eebb48f6c0603cd268b7eaf455865c64af/proposals/nnnn-async-await.md">document</a> and <a href="https://forums.swift.org/t/concurrency-asynchronous-functions/41619">forum thread</a>).</p></li></ul><h2 id="相关提案"><a href="#相关提案" class="headerlink" title="相关提案"></a>相关提案</h2><p>除本提案外，还有一些相关提案，涵盖了 Swift 并发模型的不同方面。</p><ul><li><a href="https://github.com/DougGregor/swift-evolution/blob/concurrency-objc/proposals/NNNN-concurrency-objc.md">Concurrency Interoperability with Objective-C</a>: 描述与 Objective-C 的交互，特别是接收回调的异步 Objective-C 方法与 <code>@objc async</code> Swift 方法之间的关系。</li><li><a href="https://github.com/DougGregor/swift-evolution/blob/structured-concurrency/proposals/nnnn-structured-concurrency.md">Structured Concurrency</a>：描述了异步调用所使用的任务结构、子任务和分离任务的创建、取消、优先级和其他任务管理 API。</li><li><a href="https://github.com/DougGregor/swift-evolution/blob/actors/proposals/nnnn-actors.md">Actors</a>: 描述了为并发程序提供状态隔离的 actor 模型。</li></ul><h2 id="鸣谢"><a href="#鸣谢" class="headerlink" title="鸣谢"></a>鸣谢</h2><!--The desire for async/await in Swift has been around for a long time. This proposal draws some inspiration (and most of the Motivation section) from an earlier proposal written by [Chris Lattner](https://github.com/lattner) and [Joe Groff](https://github.com/jckarter), available [here](https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619). That proposal itself is derived from a proposal written by [Oleg Andreev](https://github.com/oleganza), available [here](https://gist.github.com/oleganza/7342ed829bddd86f740a). It has been significantly rewritten (again), and many details have changed, but the core ideas of asynchronous functions have remained the same.--><p>在 Swift 中实现 async&#x2F;await 的想法由来已久。这个提案从 <a href="https://github.com/lattner">Chris Lattner</a> 和 <a href="https://github.com/jckarter">Joe Groff</a> 撰写的早期提案中获得了一些灵感(以及动机部分的大部分内容)，可以在<a href="https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619">这里</a>找到。该提案本身源自 <a href="https://github.com/oleganza">Oleg Andreev</a> 撰写的提案，可在<a href="https://gist.github.com/oleganza/7342ed829bddd86f740a">这里</a>查阅。它经过了重大的改写（再次），许多细节都发生了变化，但异步函数的核心思想没有改变。</p><!--Efficient implementation is critical for the introduction of asynchronous functions, and Swift Concurrency as a whole. Nate Chandler, Erik Eckstein, Kavon Farvardin, Joe Groff, Chris Lattner, Slava Pestov, and Arnold Schwaighofer all made significant contributions to the implementation of this proposal.--><p>高效的实现对于异步函数的引入，以及整个 Swift 并发来说都是至关重要的。Nate Chandler、Erik Eckstein、Kavon Farvardin、Joe Groff、Chris Lattner、Slava Pestov 和 Arnold Schwaighofer 都为这个提案的实现做出了重要贡献。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2021-03-06/</id>
    <link href="https://kemchenj.github.io/2021-03-06/"/>
    <published>2021-03-05T16:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md">SE-0296 async&#x2F;await</a></p>
</blockquote>
<ul>
<li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md">SE-0296</a></li>
<li>Authors: <a href="https://github.com/rjmccall">John McCall</a>, <a href="https://github.com/DougGregor">Doug Gregor</a></li>
<li>Review Manager: <a href="https://github.com/airspeedswift">Ben Cohen</a></li>
<li>Status: <strong>Implemented (Swift 5.5)</strong></li>
<li>Implementation: Available in <a href="https://swift.org/download/#snapshots">recent <code>main</code> snapshots</a> behind the flag <code>-Xfrontend -enable-experimental-concurrency</code></li>
<li>Decision Notes: <a href="https://forums.swift.org/t/accepted-with-modification-se-0296-async-await/43318">Rationale</a></li>
</ul>
<!--
Table of Contents
=================

   * [Async/await](#asyncawait)
      * [Introduction](#introduction)
      * [Motivation: Completion handlers are suboptimal](#motivation-completion-handlers-are-suboptimal)
      * [Proposed solution: async/await](#proposed-solution-asyncawait)
         * [Suspension points](#suspension-points)
      * [Detailed design](#detailed-design)
         * [Asynchronous functions](#asynchronous-functions)
         * [Asynchronous function types](#asynchronous-function-types)
         * [Await expressions](#await-expressions)
         * [Closures](#closures)
         * [Overloading and overload resolution](#overloading-and-overload-resolution)
         * [Autoclosures](#autoclosures)
         * [Protocol conformance](#protocol-conformance)
      * [Source compatibility](#source-compatibility)
      * [Effect on ABI stability](#effect-on-abi-stability)
      * [Effect on API resilience](#effect-on-api-resilience)
      * [Future Directions](#future-directions)
         * [reasync](#reasync)
      * [Alternatives Considered](#alternatives-considered)
         * [Make await imply try](#make-await-imply-try)
         * [Launching async tasks](#launching-async-tasks)
         * [Await as syntactic sugar](#await-as-syntactic-sugar)
      * [Revision history](#revision-history)
      * [Related proposals](#related-proposals)
      * [Acknowledgments](#acknowledgments)
-->

<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--
Modern Swift development involves a lot of asynchronous (or "async") programming using closures and completion handlers, but these APIs are hard to use.  This gets particularly problematic when many asynchronous operations are used, error handling is required, or control flow between asynchronous calls gets complicated.  This proposal describes a language extension to make this a lot more natural and less error prone.
-->

<p>现代 Swift 开发涉及大量使用闭包和回调的异步编程，但这些 API 很难使用。当使用了许多异步操作，需要错误处理，或者异步调用之间的控制流变得复杂时，这就变得特别麻烦。这个提案描述了一种语言扩展，使之更自然，更不容易出错。</p>
<!--
This design introduces a [coroutine model](https://en.wikipedia.org/wiki/Coroutine) to Swift. Functions can opt into being `async`, allowing the programmer to compose complex logic involving asynchronous operations using the normal control-flow mechanisms. The compiler is responsible for translating an asynchronous function into an appropriate set of closures and state machines.
-->

<p>这份设计将 <a href="https://en.wikipedia.org/wiki/Coroutine">coroutine模型</a>引入 Swift。函数可以选择成为 <code>async</code>，允许程序员使用正常的控制流机制来编写涉及异步操作的复杂逻辑。编译器负责将一个异步函数翻译成一套适当的闭包和状态机。</p>
<!--
This proposal defines the semantics of asynchronous functions. However, it does not provide concurrency: that is covered by a separate proposal to introduce structured concurrency, which associates asynchronous functions with concurrently-executing tasks and provides APIs for creating, querying, and cancelling tasks.
-->

<p>这个提案定义了异步函数的语义。然而，它并没有提供并发性：这在另一个引入结构化并发的提案里讨论，该提案将异步函数与并发执行的任务联系起来，并提供用于创建、查询和取消任务的 API。</p>
<p>Swift-evolution thread: <a href="https://forums.swift.org/t/concurrency-asynchronous-functions/41619">Pitch #1</a>, <a href="https://forums.swift.org/t/pitch-2-async-await/42420">Pitch #2</a></p>]]>
    </summary>
    <title>【译】SE-0296 Async/await</title>
    <updated>2026-09-02T11:08:19.193Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<blockquote><p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/3a358a07e878a58bec256639d2beb48461fc3177/proposals/0282-atomics.md">SE-0282 Low-Level Atomic Operations</a></p><p>译者注：</p><p>前两天 Apple 的团队发布了 <a href="https://github.com/apple/swift-atomics">swift-atomics</a>，让我们可以用纯 Swift 代码实现高性能的无锁并发数据结构，这里我就把它的前身 <a href="https://github.com/apple/swift-evolution/blob/3a358a07e878a58bec256639d2beb48461fc3177/proposals/0282-atomics.md">SE-0282 Low-Level Atomic Operations</a> 翻译出来，希望可以帮助大家更好地理解 swift-atomics，也欢迎留言指正。</p><p>本文翻译的这个版本与最终版本的提案有比较大的出入，提案<a href="https://forums.swift.org/t/se-0282-low-level-atomic-operations/35382/60">在 Joe 的提议下</a>，把 Atomics 作为一个独立的 Swift Package 发布出来，摆脱 API&#x2F;ABI 稳定的顾虑，也不需要跟着 Swift 的发布周期走，可以快速地进行迭代。</p><p>并且这个版本的提案内容最终被拆分成了三个部分进行实现：</p><ul><li>对于编译器必要的修改作为私有功能直接合并到主分支里。</li><li>对于内存模型的定义演变成 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md">SE-0282 Clarify the Swift memory consistency model ⚛︎</a> 提案，并且优化了与 C Atomics 的交互，最终提案在 Swift 5.3 里完成了实现。</li><li>核心的功能改为在 C Atomics 的基础上实现，作为 <a href="https://github.com/apple/swift-atomics">swift-atomics</a> 发布出来。</li></ul></blockquote><span id="more"></span><ul><li>Proposal: <a href="https://github.com/apple/swift-evolution/blob/3a358a07e878a58bec256639d2beb48461fc3177/proposals/0282-atomics.md">SE-0282</a></li><li>Author: <a href="https://github.com/lorentey">Karoy Lorentey</a></li><li>Review Manager: <a href="https://github.com/jckarter">Joe Groff</a></li><li>Bug: <a href="https://bugs.swift.org/browse/SR-9144">SR-9144</a></li><li>Implementation: <ul><li><a href="https://github.com/apple/swift/pull/30553">apple&#x2F;swift#30553</a> (Atomic operations)</li><li><a href="https://github.com/apple/swift/pull/26969">apple&#x2F;swift#26969</a> (Constant-constrained ordering arguments)</li></ul></li><li>Version: 2020-04-13</li><li>Status: <strong>Returned for revision</strong></li><li>Decision Notes: <a href="https://forums.swift.org/t/se-0282-low-level-atomic-operations/35382/69">Returned for revision</a></li></ul><!--*During the review process, add the following fields as needed:** Implementation: [apple/swift#NNNNN](https://github.com/apple/swift/pull/NNNNN) or [apple/swift-evolution-staging#NNNNN](https://github.com/apple/swift-evolution-staging/pull/NNNNN)* Previous Revision: [1](https://github.com/apple/swift-evolution/blob/...commit-ID.../proposals/NNNN-filename.md)* Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/)* Previous Proposal: [SE-XXXX](XXXX-filename.md)--><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><!--This proposal adds a limited set of low-level atomic operations to the Standard Library, including native spellings for C++-style memory orderings. Our goal is to enable intrepid library authors to start building synchronization constructs directly in Swift.--><p>这个提案提议给标准库添加几个 low-level 的 atomic 操作，包括 C++ 风格的原生 memory ordering。我们的目标是让框架作者可以开始在 Swift 里直接构建同步结构。</p><p>Swift-evolution thread: <a href="https://forums.swift.org/t/low-level-atomic-operations/34683">Low-Level Atomic Operations</a></p><!--As a quick taste, this is how atomics will work:--><p>作为一个前菜，我们先看一下它的使用例子：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> Atomics</span><br><span class="line"><span class="keyword">import</span> Dispatch</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> counter <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="type">DispatchQueue</span>.concurrentPerform(iterations: <span class="number">10</span>) &#123; <span class="keyword">_</span> <span class="keyword">in</span></span><br><span class="line">  <span class="keyword">for</span> <span class="keyword">_</span> <span class="keyword">in</span> <span class="number">0</span> <span class="operator">..&lt;</span> <span class="number">1_000_000</span> &#123;</span><br><span class="line">    counter.wrappingIncrement(ordering: .relaxed)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">print</span>(counter.load(ordering: .relaxed))</span><br><span class="line">counter.destroy()</span><br></pre></td></tr></table></figure><h2 id="背景说明"><a href="#背景说明" class="headerlink" title="背景说明"></a>背景说明</h2><!--In Swift today, application developers use dispatch queues and Foundation's NSLocking protocol to synchronize access to mutable state across concurrent threads of execution.--><p>目前在 Swift 里，开发者在并发线程里同步访问共享数据需要通过 DispatchQueue 或者 Foundation 的 NSLocking，</p><!--However, for Swift to be successful as a systems programming language, it needs to also provide low-level primitives that can be used to implement such synchronization constructs (and many more!) directly within Swift.--><p>然而，Swift 想要成为一门系统编程语言，还需要提供底层的接口，能够让 Swift 直接实现这种同步结构。</p><!--One such low-level primitive is the concept of an atomic value, which (in the form we propose here) has two equally important roles:--><p>其中一种底层接口就是 atomic 值，它会起到两种重要的作用：</p><!--- First, atomics introduce a limited set of types whose values provide well-defined semantics for certain kinds of concurrent access. This includes explicit support for concurrent mutations -- a concept that Swift never supported before.- Second, atomic operations come with explicit memory ordering arguments, which provide guarantees on how/when the effects of earlier or later memory accesses become visible to other threads. Such guarantees are crucial for building higher-level synchronization abstractions.--><ul><li>首先，atomics 会引入一系列新的类型，它们会给特定类型的并发访问提供明确的语义。这其中包括了显式的并发修改(concurrent mutations) – Swift 之前缺失的一个概念。</li><li>然后，atomics 操作还需要带上显式的 memory ordering 参数，这些操作带来的影响会在何时&#x2F;如何展示给之前或者之后的内存访问（同一块内存），都会由这个参数来控制并且提供保证。这样的保证对于搭建上层同步抽象来说至关重要</li></ul><!--These new primitives are intended for people who wish to implement synchronization constructs or concurrent data structures in pure Swift code. Note that this is a hazardous area that is full of pitfalls. While a well-designed atomics facility can help simplify building such tools, the goal here is merely to make it *possible* to build them, not necessarily to make it *easy* to do so. We expect that the higher-level synchronization tools that can be built on top of these atomic primitives will provide a nicer abstraction layer.--><p>这些新的原语让大家可以通过纯 Swift 代码实现同步结构或并发的数据结构。需要注意，这是一个充满陷阱的危险领域。虽然一套设计良好的 atomic 操作接口可以帮助简化实现过程，但这个提案的目标只是让这件事情变得<strong>可能</strong>，但并不一定是要让它变得<strong>容易</strong>。我们的期望是让大家可以在此基础上，搭建一个更加易用的抽象层。</p><!--We want to limit this proposal to constructs that satisfy the following requirements:--><p>我们想要给这个提案加一些约束条件，以便让它符合以下要求：</p><!--1. All atomic operations need to be explicit in Swift source, and it must be possible to easily distinguish them from regular non-atomic operations on the underlying values.2. The atomic types we provide must come with a lock-free implementation on every platform that implements them. (Platforms that are unable to provide lock-free implementations must not provide the affected constructs at all.)3. Every atomic operation must compile down to the corresponding CPU instruction (when one is available), with minimal overhead. (Ideally even if the code is compiled without optimizations.) Wait-freedom isn't a requirement -- if no direct instruction is available for an operation, then it must still be implemented, e.g. by mapping it to a compare-exchange loop.--><ol><li>所有 atomic 操作都需要在 Swift 代码里显式声明，并且可以简单地与其它非 atomic 操作的代码区分开来。</li><li>我们提供的这些 atomic 类型在能够提供支持的平台上必须使用无锁实现。（无法提供无锁实现的平台就不会提供 atomic 类型）</li><li>每个 atomic 操作都必须编译到与之对应的 CPU 指令（如果有的话），并且伴随着最小的开销。（理想情况下，即使优化过的代码也要做到这一点）。无等待则不是硬性要求 - 如果没有指令与操作一一对应的话，也必须提供实现，例如将它映射为成一个 compare-exchange 循环。</li></ol><!--Note that while this proposal doesn't include a high-level concurrency design for Swift, it also doesn't preclude the eventual addition of one. Indeed, we expect that the addition of low-level atomics will serve as an important step towards language-level concurrency, by making it easier for motivated people to explore the design space on a library level.--><p>注意，虽然这个提案不包含，但也不排斥上层的并发设计。事实上，我们希望底层 atomics 接口的加入可以让语言层面的并发设计往前迈进，通过简化这件事情让大家可以从框架层面去探索设计空间。</p><!--The implementation of the constructs introduced in this document is available at the following URL: --><p>这份文档里提及的设计实现可以在这个 URL 里看到：<a href="https://github.com/apple/swift/pull/30553">https://github.com/apple/swift/pull/30553</a></p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><!--We propose to officially adopt a C/C++-inspired memory model for Swift code:--><p>我们提议正式采纳一种类似 C&#x2F;C++ 的内存模型：</p><!--* Concurrent write/write or read/write access to the same location in memory generally remains undefined/illegal behavior, unless all such access is done through a special set of primitive *atomic operations*.* The same atomic operations can also apply *memory ordering* constraints that establish strict before/after relationships for accesses across multiple threads of execution. Such constraints can also be established by explicit *memory fences* that aren't tied to a particular atomic operation.--><ul><li>同一块内存空间的并发<strong>写&#x2F;写</strong>或<strong>读&#x2F;写</strong>访问是<strong>未定义&#x2F;非法</strong>的行为，除非所有这些访问都通过一套特殊的<strong>atomics 操作</strong>实现。</li><li>相同的 atomic 操作也可以通过 <em>memory ordering</em> 进行约束，让多线程的访问达成严格的先后顺序。这种约束也可以通过显式的 <em>memory fences</em> 来实现，不需要绑定到某个特定的 atomic 操作上。</li></ul><!--When applied carefully, atomic operations and memory ordering constraints can be used to implement higher-level synchronization algorithms that guarantee well-defined behavior for arbitrary variable accesses across multiple threads, by strictly confining their effects into some sequential timeline.--><p>恰当使用时，atomic 操作和 memory ordering 约束就可以用来实现高层级的同步算法，通过严格限制这些操作对于时间线的生效顺序(confining their effects into some sequential timeline)，保证特定变量在多线程随机访问时不会产生未定义行为，</p><!--This document does not define a formal concurrency memory model in Swift, although we believe the methodology and tooling introduced for the C++ memory model and other languages could be adapted to work for Swift, too [[C++17], [Boehm 2008], [Batty 2011], [Nienhuis 2016], [Mattarei 2018]]. --><p>虽然我们相信引入了 C++ 的内存模型之后，其它语言的并发内存模型经验和工具也可以应用到 Swift 里，但这份文档并没有正式定义 Swift 的并发内存模型。</p><!--For now, we will be heavily relying on the Law of Exclusivity as defined in [[SE-0176]] and the [[Ownership Manifesto]], and we'll provide informal descriptions of how memory orderings interact with Swift's language features. The intention is that Swift's memory orderings will be fully interoperable with their C/C++ counterparts.--><p>目前来说，我们将会重度依赖于 [<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md">SE-0176</a>] 和 [<a href="https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md">Ownership Manifesto</a>] 里定义的独占性原则，并且我们会仔细说明 memory orderings 会怎么跟 Swift 的语言功能交互。目的是让 Swift 的 memory ordering 与 C&#x2F;C++ 对应的部分完全兼容。</p><h3 id="Atomic-模块"><a href="#Atomic-模块" class="headerlink" title="Atomic 模块"></a>Atomic 模块</h3><!--While most Swift programs won't directly use the new atomic primitives, we still consider the new constructs to be an integral part of the core Standard Library.--><p>虽然大部分 Swift 程序都不会直接使用新的 atomic 原语，但我们还是将它看作是 Swift 核心标准库必要的组成部分。</p><!-- * The implementation of atomic operations needs access to compiler intrinsics that are only exposed to the Standard Library. * The memory orderings introduced here define a concurrency memory model for Swift code that has implications on the language as a whole. (Fortunately, Swift is already designed to interoperate with the C/C++ memory model, so introducing a subset of C++ memory orderings in the Standard Library doesn't by itself require language-level changes.)--><ul><li>atomic 操作的实现需要使用那些只暴露给标准库的编译器接口。</li><li>总体上来看，这里引入的 Memory orderings 为 Swift 定义了一种并发内存模型。（幸运的是，Swift 的设计已经可以跟 C&#x2F;C++ 内存模型进行无缝交互，所以将 C++ Memory Ordering 的子集引入标准库并不需要做语言层面的改动）</li></ul><!--That said, it seems highly undesirable to add low-level atomics to the default namespace of every Swift program, so we propose to place the atomic constructs in a new Standard Library module called `Atomics`. Code that needs to use low-level atomics will need to explicitly import the new module:--><p>也就是说，把底层的 atomics 操作加到每一个 Swift 程序默认的命名空间下似乎不那么合适，所以我们提议将 atomic 接口放到一个新的 Swift 标准库模块 <code>Atomics</code> 里。需要使用底层 atomics 时需要显式地导入这个新模块：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> Atomics</span><br></pre></td></tr></table></figure><!--We expect that most Swift projects will use atomic operations only indirectly, through higher-level synchronization constructs. Therefore, importing the Atomics module will be a relatively rare occurrence, mostly limited to projects that implement such tools.--><p>我们认为大部分 Swift 项目，都只会通过更高层级的同步结构间接使用 atomics 操作。所以，很少会需要导入 Atomics 模块，只有少数实现了高层级同步结构的项目需要导入。</p><!--In this proposal, we are adding support for atomic operations on a small set of basic types.--><p>在这个提案里，我们会给 atomic 操作增加一个基本类型集合。</p><!--All of these are covered by a single generic struct called `UnsafeAtomic` that implements an **unsafe reference type** holding a single, untagged primitive value of some atomic type:--><p>所有的这些都会通过一个泛型结构体 <code>UnsafeAtomic</code> 实现，它实现了一个 <strong>unsafe 引用类型</strong>，持有一个原始 atomic 类型的 untagged 的值(a single, untagged primitive value of some atomic type)：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UnsafeAtomic</span>&lt;<span class="type">Value</span>: <span class="type">AtomicProtocol</span>&gt; &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--The full set of atomic types introduced in this proposal includes- all standard fixed-width integer types, - standard pointer types and unmanaged references,- optional pointers and optional unmanaged references,- custom types that are raw-representable with an atomic type.--><p>这个提案里引入的 atomic 类型包括：</p><ul><li>所有标准的定长整型类型，</li><li>标准的指针类型和 unmanaged 引用，</li><li>Optional 的指针和 Optional 的 unmanaged 引用，</li><li>atomic 的 raw-representable 自定义类型。</li></ul><!--Here is a list of declarations demonstrating the variety of types supported:--><p>下面是完整的列表：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Standard signed integers:</span></span><br><span class="line"><span class="keyword">let</span>   i: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> i64: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int64</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> i32: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int32</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> i16: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int16</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span>  i8: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int8</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Standard unsigned integers:</span></span><br><span class="line"><span class="keyword">let</span>   u: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UInt</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> u64: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UInt64</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> u32: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UInt32</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> u16: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UInt16</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span>  u8: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UInt8</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Standard unsafe pointers:</span></span><br><span class="line"><span class="keyword">let</span>   r: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UnsafeRawPointer</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span>  mr: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UnsafeMutableRawPointer</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span>   p: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UnsafePointer</span>&lt;<span class="type">T</span>&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span>  mp: <span class="type">UnsafeAtomic</span>&lt;<span class="type">UnsafeMutablePointer</span>&lt;<span class="type">T</span>&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Standard optional unsafe pointers:</span></span><br><span class="line"><span class="keyword">let</span>  or: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Optional</span>&lt;<span class="type">UnsafeRawPointer</span>&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> omr: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Optional</span>&lt;<span class="type">UnsafeMutableRawPointer</span>&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span>  op: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Optional</span>&lt;<span class="type">UnsafePointer</span>&lt;<span class="type">T</span>&gt;&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span> omp: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Optional</span>&lt;<span class="type">UnsafeMutablePointer</span>&lt;<span class="type">T</span>&gt;&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Unmanaged references:</span></span><br><span class="line"><span class="keyword">let</span>   u: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Unmanaged</span>&lt;<span class="type">T</span>&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">let</span>  ou: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Optional</span>&lt;<span class="type">Unmanaged</span>&lt;<span class="type">T</span>&gt;&gt;&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Custom atomic representable types:</span></span><br><span class="line"><span class="keyword">enum</span> <span class="title class_">MyState</span>: <span class="title class_ inherited__">Int</span>, <span class="title class_ inherited__">AtomicProtocol</span> &#123;</span><br><span class="line">  <span class="keyword">case</span> starting</span><br><span class="line">  <span class="keyword">case</span> running</span><br><span class="line">  <span class="keyword">case</span> stopped</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span>  ar: <span class="type">UnsafeAtomic</span>&lt;<span class="type">MyState</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br></pre></td></tr></table></figure><!--As a special case, we are also introducing a lazily initializable but otherwise read-only atomic strong reference construct. This is unlike the others in that it offers a heavily restricted set of operations, and it is implemented by a standalone generic struct:--><p>作为一个特例，我们也需要引入一个懒加载，只读的 atomic 强引用结构。与其他类型不同，它提供的操作非常有限，并且它是使用一个独立的泛型结构体实现的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UnsafeAtomicLazyReference</span>&lt;<span class="type">Instance</span>: <span class="type">AnyObject</span>&gt;</span><br></pre></td></tr></table></figure><!--Most of these initial atomic types are built around "single-width" atomic operations -- meaning that all operations can be implemented using underlying compiler intrinsics that operate on *at most* a single, pointer-sized integer value. (The exceptions are `Int64` and `UInt64` on 32-bit platforms, which require double-wide atomics.)--><p>这些基本的 Atomic 类型大部分都是围绕着“单精度”的 atomic 操作构建起来的 —— 意味着所有操作都可以让编译器实现为单精度整型指针的操作。（<code>Int64</code> 和 <code>UInt64</code> 在 32-bit 平台上的情况比较特殊，需要双精度的 atmoics）</p><!--Atomic operations for the pointer and reference types above could be implemented as mere convenience wrappers around atomic `Int` operations. In theory, we could therefore omit them without loss of performance or generality. However, in practice, we expect users will need to build abstractions for atomic pointers anyway, and it makes sense to standardize APIs to unify terminology, eliminate boilerplate and to prevent confusion across projects. By providing implementations for these directly in the Standard Library, we are able to add custom `AtomicProtocol` conformances to integrate them directly into `UnsafeAtomic`. We are adding support for custom atomic-representable types for the same reason.--><p>指针和引用类型的 atomic 操作都可以视作是 atmoic 的 <code>Int</code> 操作来实现。所以理论上我们可以忽略掉它，并且不会造成任何性能和泛用性上的损失。然而在实践中，我们认为用户无论如何都会给 atomic 指针建立抽象，所以为此建立标准化的 API 也非常合理，减少重复劳动和模版代码，并且也减少了项目间不必要的定义差异。通过标准库直接提供实现，用户可以给自定义的类型添加 <code>AtomicProtocol</code> 的 conformance，让它们可以直接装进 <code>UnsafeAtomic</code> 里。同样的，我们也会给自定义的 atomic-representable 类型提供支持。</p><!--Notably, none of these atomic types support composite values -- they provide no direct support for storing additional information (such as a version stamp) alongside the primary value. See the section on [*Double-Wide Atomics*](#double-wide-atomics-and-the-aba-problem) for some important constructs that we may want to add later. Our expectation is that the experience we'll gain with this initial batch will inform the design of those potential future additions.--><p>特别的是，这些 atomic 类型都没有提供组合值 —— 它们不会存储任何附带信息（例如版本标签）。<a href="#%E5%8F%8C%E7%B2%BE%E5%BA%A6-Atomics-%E5%92%8C-ABA-%E9%97%AE%E9%A2%98"><em>双精度 Atomics</em></a> 小节里介绍了一些我们之后可能会想要引入的类型。我们的期望是这个初始提案的经验，可以帮助到未来的设计。</p><!--The `Atomics` module also defines three enum-like structs representing the three flavors of memory orderings, and a standalone top-level function for issuing memory barriers. We'll describe these in [*Atomic Memory Orderings*](#atomic-memory-orderings).--><p><code>Atomics</code> 模块也定义三个 enum-like 的结构体来表示三种风格的 memory orderings，并且有一个独立的全局函数来负责 memory barriers。我们在 <a href="#atomic-memory-orderings"><em>Atomic Memory Orderings</em></a> 里有详细的介绍。</p><h3 id="内存管理（缺乏）"><a href="#内存管理（缺乏）" class="headerlink" title="内存管理（缺乏）"></a>内存管理（缺乏）</h3><!--As implied by the `Unsafe` prefix, the new atomic constructs do not provide automated memory management for the memory location that holds their value. Both unsafe atomic types provide an `init(at:)` initializer that takes a pointer to appropriately initialized storage.--><p><code>Unsafe</code> 的前缀意味着新的 atomic 结构并不支持自动内存管理。同样的，提供了 <code>init(at:)</code> 构造器的 unsafe atomic 类型也不会：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">UnsafeAtomic</span>&lt;<span class="type">Value</span>: <span class="type">AtomicProtocol</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Storage</span> &#123;</span><br><span class="line">    <span class="comment">// Transform `value` into a new storage instance.</span></span><br><span class="line">    <span class="keyword">init</span>(<span class="keyword">_</span> <span class="params">value</span>: __owned <span class="type">Value</span>)</span><br><span class="line">    <span class="comment">// Dispose of this storage instance, returning the final value it represents.</span></span><br><span class="line">    <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">dispose</span>() -&gt; <span class="type">Value</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">init</span>(<span class="params">at</span> <span class="params">address</span>: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Storage</span>&gt;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">UnsafeAtomicLazyReference</span>&lt;<span class="type">Instance</span>: <span class="type">AnyObject</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Storage</span> &#123;</span><br><span class="line">    <span class="keyword">init</span>()</span><br><span class="line">    <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">dispose</span>() -&gt; <span class="type">Instance</span>?</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">init</span>(<span class="params">at</span> <span class="params">address</span>: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Storage</span>&gt;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Code that uses these unsafe atomic types must manually manage the lifecycle of the underlying memory location to ensure1. that it is bound to the correct `Storage` type,2. that it is initialized to a well-defined value through `Storage.init(_:)`,3. that the location remains valid while it is being accessed through atomic operations, and4. that the storage is properly disposed of (using `Storage.dispose()`) before the memory location is destroyed.--><p>使用了这些 unsafe atomic 类型的代码必须手动管理它们的生命周期去保证</p><ol><li>它通过正确的 <code>Storage</code> 类型提供边界，</li><li>它通过 <code>Storage.init(_:)</code> 初始化了一个定义清晰的值，</li><li>它通过 atomic 操作访问时，对应位置的值始终是合法的，并且</li><li>它在内存被回收之前，storage 对应的值必须被正确地回收（通过 <code>Storage.dispose()</code>）。</li></ol><!--This is typically done by allocating a dynamic variable dedicated to holding storage for the atomic value:--><p>以上这些通常会通过分配一个专门用来存储 atomic 值的动态变量来完成：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Create an unsafe atomic integer, initialized to 0 </span></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">atomicDemo</span>&lt;<span class="type">Value</span>: <span class="type">AtomicProtocol</span>&gt;(<span class="params">initialValue</span>: <span class="type">Value</span>) &#123;</span><br><span class="line">  <span class="keyword">typealias</span> <span class="type">Storage</span> <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Value</span>&gt;.<span class="type">Storage</span></span><br><span class="line">  <span class="keyword">let</span> ptr <span class="operator">=</span> <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Storage</span>&gt;.allocate(capacity: <span class="number">1</span>)</span><br><span class="line">  ptr.initialize(to: <span class="type">Storage</span>(initialValue))</span><br><span class="line">  <span class="keyword">let</span> atomic <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;(at: ptr)</span><br><span class="line"></span><br><span class="line">  <span class="operator">...</span> <span class="comment">// Use `atomic`</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// Destroy it</span></span><br><span class="line">  <span class="keyword">_</span> <span class="operator">=</span> ptr.pointee.dispose()</span><br><span class="line">  ptr.deinitialize(count: <span class="number">1</span>)</span><br><span class="line">  ptr.deallocate()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--In fact, this is such a commonly reoccurring pattern that both `UnsafeAtomic*` types provide a couple of convenience methods to do it for us:--><p>实际上，这是一种很常见的模式，所以全部 <code>UnsafeAtomic*</code> 类型都为此提供了便捷方法：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> &#123;</span><br><span class="line">  <span class="comment">// Dynamically allocates &amp; initializes storage</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">create</span>(<span class="params">initialValue</span>: __owned <span class="type">Value</span>) -&gt; <span class="keyword">Self</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Deinitializes and deallocates storage, returning final value</span></span><br><span class="line">  <span class="keyword">@discardableResult</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">destroy</span>() -&gt; <span class="type">Value</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomicLazyReference</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">create</span>() -&gt; <span class="keyword">Self</span> <span class="comment">// Initializes to `nil`</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">create</span>() -&gt; <span class="keyword">Self</span> <span class="comment">// 初始化为 `nil`</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">@discardableResult</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">destroy</span>() -&gt; <span class="type">Instance</span>?</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--We can use these to improve readability:--><p>我们可以使用这些来提高可读性：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> atomic <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Value</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line"><span class="operator">...</span> <span class="comment">// Use `atomic`</span></span><br><span class="line">atomic.destroy()</span><br></pre></td></tr></table></figure><!--Consistent use of `create`/`destroy` makes it far easier to audit code that manages the lifetime of these constructs. For example, in the typical case where `UnsafeAtomic` values are used as class instance variables, we expect to see a call to `create` during initialization, and a call to `destroy` in `deinit`:--><p>成对使用 <code>create</code>&#x2F;<code>destroy</code> 可以简化对象生命周期的管理。例如，典型情况下 <code>UnsafeAtomic</code> 值会在作为类的实例属性使用，我们期望看到 <code>init</code> 时调用 <code>create</code>，<code>deinit</code> 时调用 <code>destroy</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">AtomicCounter</span> &#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">let</span> _value <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">deinit</span> &#123;</span><br><span class="line">    _value.destroy()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">increment</span>() &#123;</span><br><span class="line">    _value.wrappingIncrement(by: <span class="number">1</span>, ordering: .relaxed)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">get</span>() -&gt; <span class="type">Int</span> &#123;</span><br><span class="line">    _value.load(ordering: .relaxed)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--While `create`/`destroy` are convenient, the ability to manually control the storage location is critical for usecases where a separate allocation for every atomic value would be wasteful. (For example, these usecases can use `ManagedBuffer` APIs to create atomic storage directly within a class instance.)--><p>虽然 <code>create</code>&#x2F;<code>destroy</code> 很方便，但手动控制存储位置也很有必要，有些使用场景下分别为每个 atomic 值申请内存会显得浪费。（例如，这些用例可以使用 <code>ManageBuffer</code> 接口来直接创建atomic 存储空间）</p><blockquote><p>译者注：这里的意思应该是，如果同时要用多个 atomic 值，为每个值分别分配内存不如一次性分配。</p></blockquote><!--Now that we know how to create and destroy atomic values, it's time to introduce some actual atomic operations.--><p>现在我们知道如何创建和销毁 atomic 值，该开始介绍实际使用的 atomic 操作了。</p><h3 id="基础的-Atomic-操作"><a href="#基础的-Atomic-操作" class="headerlink" title="基础的 Atomic 操作"></a>基础的 Atomic 操作</h3><!--`UnsafeAtomic` provides six basic atomic operations for all supported types:--><p><code>UnsafeAtomic</code> 给所有支持类型提供了六个基础的 atomic 操作：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> &#123;</span><br><span class="line">  <span class="comment">// Atomically load and return the current value.</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">load</span>(<span class="params">ordering</span>: <span class="type">AtomicLoadOrdering</span>) -&gt; <span class="type">Value</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Atomically update the current value.</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">store</span>(<span class="keyword">_</span> <span class="params">desired</span>: __owned <span class="type">Value</span>, <span class="params">ordering</span>: <span class="type">AtomicStoreOrdering</span>)</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Atomically update the current value, returning the original value.</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">exchange</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">desired</span>: __owned <span class="type">Value</span>, </span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">compareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: __owned <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">compareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: __owned <span class="type">Value</span>,</span><br><span class="line">    <span class="params">successOrdering</span>: <span class="type">AtomicUpdateOrdering</span>,</span><br><span class="line">    <span class="params">failureOrdering</span>: <span class="type">AtomicLoadOrdering</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">weakCompareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: __owned <span class="type">Value</span>,</span><br><span class="line">    <span class="params">successOrdering</span>: <span class="type">AtomicUpdateOrdering</span>,</span><br><span class="line">    <span class="params">failureOrdering</span>: <span class="type">AtomicLoadOrdering</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The `ordering` arguments indicate if the atomic operation is also expected to synchronize the effects of previous (or subsequent) accesses. This is explained in a separate section below.--><p><code>ordering</code> 参数用来表示 atomic 操作是否需要同步前一个（或者后一个）访问带来的效用。在下面有一个单独的小节进行解释。</p><!--The first three operations are relatively simple:--><p>前三个操作相对比较简单：</p><!--- `load` returns the current value.- `store` updates it.- `exchange` is a combination of `load` and `store`; it updates the  current value and returns the previous one as a single atomic  operation.--><ul><li><code>load</code> 返回当前值。</li><li><code>store</code> 更新当前值。</li><li><code>exchange</code> 是 <code>load</code> 和 <code>store</code> 的结合；它会更新当前值并且返回旧值，并且整体会作为<strong>一个</strong> atomic 操作完成。</li></ul><!--The three `compareExchange` variants are somewhat more complicated: they implement a version of `exchange` that only performs the update if the original value is the same as a supplied expected value. To be specific, they execute the following algorithm as a single atomic transaction:--><p>接下来三种 <code>compareExchange</code> 的变种就相对比较复杂 ：它们会先比较新旧值，只有新旧值不同时才会执行 <code>exchange</code> 的操作。具体来说，它们会在一个 atomic 事务里完成下面的逻辑：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">guard</span> currentValue <span class="operator">==</span> expected <span class="keyword">else</span> &#123; </span><br><span class="line">  <span class="keyword">return</span> (exchanged: <span class="literal">false</span>, original: currentValue) </span><br><span class="line">&#125;</span><br><span class="line">currentValue <span class="operator">=</span> desired</span><br><span class="line"><span class="keyword">return</span> (exchanged: <span class="literal">true</span>, original: expected)</span><br></pre></td></tr></table></figure><!--All three variants implement the same algorithm. The first variant uses the same memory ordering whether or not the exchange succeeds, while the other two allow callers to specify two distinct memory orderings for the success and failure cases. The two orderings are independent from each other -- all combinations of update/load orderings are supported [[P0418]]. (Of course, the implementation may need to "round up" to the nearest ordering combination that is supported by the underlying code generation layer and the targeted CPU architecture.)--><p>这三个变种实现了相同的算法。第一个变种使用同样的 memory order，无论 exchange 是否成功，而另外两个则允许调用者给成功和失败的情况分别指定 memory ordering。这两种顺序各自独立 —— update&#x2F;load 所有 orderings 的组合都是支持的 [<a href="http://wg21.link/P0418">P0418</a>]。（当然，可能需要根据代码生成和目标的 CPU 架构，改为最近似的可用的 ordering）</p><!--The `weakCompareExchange` form may sometimes return false even when the original and expected values are equal. (Such failures may happen when some transient condition prevents the underlying operation from succeeding -- such as an incoming interrupt during a load-link/store-conditional instruction sequence.) This variant is designed to be called in a loop that only exits when the exchange is successful; in such loops using `weakCompareExchange` may lead to a performance improvement by eliminating a nested loop in the regular, "strong", `compareExchange` variants.--><p><code>weakCompareExchange</code> 的形式偶尔在 original 和 expected 相等时也会返回 <code>false</code>。（这种情况会在底层操作处于某些中间状态时出现 —— 例如在 load-link&#x2F;store-conditional 的指令序列里突然从外部插入了一个中断），这种变种是为了在循环中调用，只有 exchange 成功时才退出循环；在这种循环里使用 <code>weakCompareExchange</code> 可能会有性能提升，因为它可以避免使用 “strong” 的 <code>compareExchange</code> 时造成的循环嵌套。</p><blockquote><p>译者注：具体的细节可以参考 <a href="https://stackoverflow.com/questions/25199838/understanding-stdatomiccompare-exchange-weak-in-c11">Understanding std::atomic::compare_exchange_weak() in C++11</a></p></blockquote><!--The compare-exchange primitive is special: it is a universal operation that can be used to implement all other atomic operations, and more. For example, here is how we could use `compareExchange` to implement a wrapping increment operation over `UnsafeAtomic<Int>` values:--><p>这种 compare-exchange 原语是很特殊的，因为它通用到可以用来实现其它所有 atomic 操作。例如，下面展示了我们可以如何通过 <code>compareExchange</code> 给 <code>UnsafeAtomic&lt;Int&gt;</code> 实现 increment 操作：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> <span class="keyword">where</span> <span class="type">Value</span> <span class="operator">==</span> <span class="type">Int</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">wrappingIncrement</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">operand</span>: <span class="type">Int</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) &#123;</span><br><span class="line">    <span class="keyword">var</span> done <span class="operator">=</span> <span class="literal">false</span></span><br><span class="line">    <span class="keyword">var</span> current <span class="operator">=</span> load(ordering: .relaxed)</span><br><span class="line">    <span class="keyword">while</span> <span class="operator">!</span>done &#123;</span><br><span class="line">      (done, current) <span class="operator">=</span> compareExchange(</span><br><span class="line">        expected: current,</span><br><span class="line">        desired: current <span class="operator">&amp;+</span> operand,</span><br><span class="line">        ordering: ordering)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="专门的-Integer-操作"><a href="#专门的-Integer-操作" class="headerlink" title="专门的 Integer 操作"></a>专门的 Integer 操作</h3><!--Most CPU architectures provide dedicated atomic instructions for certain integer operations, and these are generally more efficient than implementations using `compareExchange`. Therefore, it makes sense to expose a set of dedicated methods for common integer operations so that these will always get compiled into the most efficient implementation available.--><p>大多数的 CPU 架构都提供了专用的 atmoic 指令，用于特定的整型操作，通常它们都比 <code>compareExchange</code> 的实现更加高效。所以，把这一系列的整型操作都暴露出来，可以让更高效的实现变得可能。</p><!--These specialized integer operations generally come in two variants, based on whether they're returning the value before or after the operation:--><p>这些专门的整型操作通常有两种变形，基于它们是否会在操作前或操作后返回值：</p><table><thead><tr><th>Method Name 方法名</th><th>Returns 返回值</th><th>Implements 实现</th></tr></thead><tbody><tr><td><code>loadThenWrappingIncrement(by:ordering:)</code></td><td>original value</td><td><code>a &amp;+= b</code></td></tr><tr><td><code>loadThenWrappingDecrement(by:ordering:)</code></td><td>original value</td><td><code>a &amp;-= b</code></td></tr><tr><td><code>loadThenBitwiseAnd(with:ordering)</code></td><td>original value</td><td><code>a &amp;= b</code></td></tr><tr><td><code>loadThenBitwiseOr(with:ordering)</code></td><td>original value</td><td><code>a |= b</code></td></tr><tr><td><code>loadThenBitwiseXor(with:ordering)</code></td><td>original value</td><td><code>a ^= b</code></td></tr><tr><td><code>wrappingIncrementThenLoad(by:ordering:)</code></td><td>new value</td><td><code>a &amp;+= b</code></td></tr><tr><td><code>wrappingDecrementThenLoad(by:ordering:)</code></td><td>new value</td><td><code>a &amp;-= b</code></td></tr><tr><td><code>bitwiseAndThenLoad(with:ordering)</code></td><td>new value</td><td><code>a &amp;= b</code></td></tr><tr><td><code>bitwiseOrThenLoad(with:ordering)</code></td><td>new value</td><td><code>a |= b</code></td></tr><tr><td><code>bitwiseXorThenLoad(with:ordering)</code></td><td>new value</td><td><code>a ^= b</code></td></tr><tr><td><code>wrappingIncrement(by:ordering:)</code></td><td>none</td><td><code>a &amp;+= b</code></td></tr><tr><td><code>wrappingDecrement(by:ordering:)</code></td><td>none</td><td><code>a &amp;-= b</code></td></tr></tbody></table><!--The `wrappingIncrement` and `wrappingDecrement` operations are provided as a convenience for incrementing/decrementing values in the common case when a return value is not required.--><p><code>wrappingIncrement</code> 和 <code>wrappingDecrement</code> 操作是为了自增&#x2F;自减提供的便捷方法（不需要返回值时）。</p><!--While we require all atomic operations to be free of locks, we don't require wait-freedom. Therefore, on architectures that don't provide direct hardware support for some or all of these operations, we still require them to be implemented using `compareExchange` loops like the one for `wrappingIncrement` above.--><p>虽然我们要求所有 atomic 操作都无需加锁，但并不要求无等待（wait-free）。所以，在那些无法为操作直接提供硬件支持的架构上，我们会要求它们使用 <code>compareExchange</code> 循环来实现上述的操作。</p><!--`UnsafeAtomic<Value>` exposes these operations when `Value` conforms to the `AtomicInteger` protocol, which all standard fixed-width integer types do.--><p><code>UnsafeAtomic&lt;Value&gt;</code> 在 <code>Value</code> 遵循 <code>AtomicInteger</code> 协议时会提供下列方法，并且所有标准定长的整数类型都满足这个条件：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> <span class="keyword">where</span> <span class="type">Value</span>: <span class="type">AtomicInteger</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">loadThenWrappingIncrement</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">delta</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">bitwiseOrThenLoad</span>(</span><br><span class="line">    <span class="params">with</span> <span class="params">value</span>: <span class="type">Value</span>, </span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">wrappingIncrement</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">delta</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> counter <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line"><span class="keyword">defer</span> &#123; counter.destroy() &#125;</span><br><span class="line">counter.wrappingIncrement(by: <span class="number">42</span>, ordering: .relaxed)</span><br></pre></td></tr></table></figure><h3 id="Atomic-Lazy-References"><a href="#Atomic-Lazy-References" class="headerlink" title="Atomic Lazy References"></a>Atomic Lazy References</h3><!--The operations provided by `UnsafeAtomic<Unmanaged<T>>` only operate on the unmanaged reference itself. They don't allow us directly access to the referenced object -- we need to manually invoke the methods `Unmanaged` provides for this purpose (usually, `takeUnretainedValue`).--><p><code>UnsafeAtomic&lt;Unmanaged&lt;T&gt;&gt;</code> 提供的操作只能用于 unmanaged 引用自身。它们不允许我们直接访问引用的对象 —— 这种情况下我们需要手动触发 <code>Unmanaged</code> 为此提供的函数（通常是 <code>takeUnretainedValue</code>）。</p><!--Note that loading the atomic unmanaged reference and converting it to a strong reference are two distinct operations that won't execute as a single atomic transaction. This can easily lead to race conditions when a thread releases an object while another is busy loading it:--><p>注意，读取 atomic unmanaged 引用和将它转化为强引用是两个独立的操作，它们无法在一个 atomic 事务操作里完成。当其它线程释放了对象，当前线程又读取它的时候就会产生竞态：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// BROKEN CODE. DO NOT EMULATE IN PRODUCTION.</span></span><br><span class="line"><span class="keyword">let</span> myAtomicRef <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Unmanaged</span>&lt;<span class="type">Foo</span>&gt;&gt;.create(initialValue: <span class="operator">...</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Thread A: Load the unmanaged value and then convert it to a regular</span></span><br><span class="line"><span class="comment">//           strong reference.</span></span><br><span class="line"><span class="keyword">let</span> ref <span class="operator">=</span> myAtomicRef.load(ordering: .acquiring).takeUnretainedValue()</span><br><span class="line"><span class="operator">...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Thread B: Store a new reference in the atomic unmanaged value and </span></span><br><span class="line"><span class="comment">//           release the previous reference.</span></span><br><span class="line"><span class="keyword">let</span> new <span class="operator">=</span> <span class="type">Unmanaged</span>.passRetained(<span class="operator">...</span>)</span><br><span class="line"><span class="keyword">let</span> old <span class="operator">=</span> myAtomicRef.exchange(new, ordering: .acquiringAndReleasing)</span><br><span class="line">old.release() <span class="comment">// RACE CONDITION</span></span><br></pre></td></tr></table></figure><!--If thread B happens to release the same object that thread A is in the process of loading, then thread A's `takeUnretainedValue` may attempt to retain a deallocated object.--><p>如果线程 B 释放的对象刚好是线程 A 正在加载的，那此时线程 A 的 <code>takeUnretainedValue</code> 也许会 retain 一个已经释放掉的对象。</p><!--Such problems make `UnsafeAtomic<Unmanaged<T>>` exceedingly difficult to use in all but the simplest situations. The section on [*Atomic Strong References*](#atomic-strong-references-and-the-problem-of-memory-reclamation) below describes some new constructs we may introduce in future proposals to assist with this issue.--><p>这种问题让 <code>UnsafeAtomic&lt;Unmanaged&lt;T&gt;&gt;</code> 在所有场景下都难以使用。下面 <a href="#Atomic-%E5%BC%BA%E5%BC%95%E7%94%A8%E5%92%8C-Memory-Reclamation-%E9%97%AE%E9%A2%98"><em>Atomic 强引用</em></a> 小节里会介绍我们未来为了解决这个问题可能引入的方案。</p><!--For now, we provide the standalone type `UnsafeAtomicLazyReference`; this is an example of a useful construct that could be built on top of `UnsafeAtomic<Unmanaged>` operations. (Of all the various atomic constructs introduced in this proposal, only `UnsafeAtomicLazyReference` represents a regular strong reference to a class instance -- the other pointer/reference types leave memory management entirely up to the user.)--><p>就目前来说，我们会提供一个独立的类型 <code>UnsafeAtomicLazyReference</code> 来处理这种场景，它是在 <code>UnsafeAtomic&lt;Unmanaged&gt;</code> 的基础上封装实现的。（这个提案里引入的所有 atomic 类型，只有 <code>UnsafeAtomicLazyReference</code> 像一个常规的类实例的引用一样 —— 其它的指针&#x2F;引用类型都需要让用户手动管理内存。）</p><!--An `UnsafeAtomicLazyReference` holds an optional reference that is initially set to `nil`. The value can be set exactly once, but it can be read an arbitrary number of times. Attempts to change the value after the first `storeIfNilThenLoad` call are ignored, and return the current value instead.--><p>一个 <code>UnsafeAtomicLazyReference</code> 会持有一个 optional 引用并且将它的初始值设为 <code>nil</code>。这个引用之后只能再设置一次，但可以进行任意次数的读取。<code>storeIfNilThenLoad</code> 只会在第一次调用时生效，后续的调用都会被忽略，并且返回当前的值。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">UnsafeAtomicLazyReference</span>&lt;<span class="type">Instance</span>: <span class="type">AnyObject</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">typealias</span> <span class="type">Value</span> <span class="operator">=</span> <span class="type">Instance</span>?</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Storage</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">init</span>()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">@discardableResult</span> </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">dispose</span>() -&gt; <span class="type">Value</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">init</span>(<span class="params">at</span> <span class="params">address</span>: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Storage</span>&gt;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">create</span>() -&gt; <span class="keyword">Self</span></span><br><span class="line">  <span class="keyword">@discardableResult</span> </span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">destroy</span>() -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">storeIfNilThenLoad</span>(<span class="keyword">_</span> <span class="params">desired</span>: __owned <span class="type">Instance</span>) -&gt; <span class="type">Instance</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">load</span>() -&gt; <span class="type">Instance</span>?</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This is the only atomic type in this proposal that doesn't provide the usual `load`/`store`/`exchange`/`compareExchange` operations.--><p>这是提案里唯一一个不提供 <code>load</code>&#x2F;<code>store</code>&#x2F;<code>exchange</code>&#x2F;<code>compareExchange</code> 操作的 atomic 类型。</p><!--This construct allows library authors to implement a thread-safe lazy initialization pattern:--><p>这个结构可以让库作者实现一个线程安全，懒加载的模式：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> _foo: <span class="type">UnsafeAtomicLazyReference</span>&lt;<span class="type">Foo</span>&gt; <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// This is safe to call concurrently from multiple threads.</span></span><br><span class="line"><span class="keyword">var</span> atomicLazyFoo: <span class="type">Foo</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">let</span> foo <span class="operator">=</span> _foo.load() &#123; <span class="keyword">return</span> foo &#125;</span><br><span class="line">  <span class="comment">// Note: the code here may run concurrently on multiple threads.</span></span><br><span class="line">  <span class="comment">// All but one of the resulting values will be discarded.</span></span><br><span class="line">  <span class="keyword">let</span> foo <span class="operator">=</span> <span class="type">Foo</span>()</span><br><span class="line">  <span class="keyword">return</span> _foo.storeIfNilThenLoad(foo)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The Standard Library has been internally using such a pattern to implement deferred bridging for `Array`, `Dictionary` and `Set`.--><p>标准库内部已经通过这种模式来实现 <code>Array</code>, <code>Dictionary</code> 和 <code>Set</code> 的延迟桥接。</p><!--Note that unlike the rest of the atomic types, `load` and `storeIfNilThenLoad(_:)` do not expose `ordering` parameters. (Internally, they map to acquiring/releasing operations to guarantee correct synchronization.)--><p>注意，它并不像其它 atomic 类型那样，<code>load</code> 和 <code>storeIfNilThenLoad(_:)</code> 并没有提供 <code>ordering</code> 参数。（在内部，它们会映射为抢占&#x2F;释放操作来保证正确的顺序）</p><h3 id="Atomic-Memory-Orderings"><a href="#Atomic-Memory-Orderings" class="headerlink" title="Atomic Memory Orderings"></a>Atomic Memory Orderings</h3><!--To enable the implementation of synchronization constructs in pure Swift code, we must introduce a memory consistency model in the language. Luckily, Swift already interoperates with the C/C++ memory model, so it seems reasonable to adopt a C/C++-style memory model based on acquire and release orderings. In this model, concurrent access to shared state remains undefined behavior unless all such access is forced into a conflict-free timeline through explicit synchronization operations.--><p>想要使用纯 Swift 代码实现同步结构的话，我们必须给语言引入一套内存一致性模型。幸运的是，Swift 已经可以跟 C&#x2F;C++ 的内存模型进行无缝交互，所以基于抢占&#x2F;释放的顺序，直接采用 C&#x2F;C++ 风格的内存模型也很合理。在这个模型里，并发访问共享状态依旧是未定义行为，除非所有访问都通过显式的同步操作强制合并到一条无冲突的时间线里。</p><!--The atomic constructs above implement concurrent read/write access by mapping to atomic instructions in the underlying architecture. All accesses of a particular atomic value get serialized into some global sequential timeline, no matter what thread executed them.--><p>前面介绍的 atomic 结构会将并发读&#x2F;写访问映射为底层架构对应的 atomic 指令。无论从哪个线程执行，特定 atomic 值的全部访问都会被插入到某个连续的全局时间线里。</p><!--However, this alone does not give us a way to synchronize accesses to regular variables, or between atomic accesses to different memory locations. To support such synchronization, each atomic operation can be configured to also act as a synchronization point for other variable accesses within the same thread, preventing previous accesses from getting executed after the atomic operation, and/or vice versa. Atomic operations on another thread can then synchronize with the same point, establishing a strict (although partial) timeline between accesses performed by both threads. This way, we can reason about the possible ordering of operations across threads, even if we know nothing about how those operations are implemented. (This is how locks or dispatch queues can be used to serialize the execution of arbitrary blocks containing regular accesses to shared variables.) For more details, see \[[C++17], [N2153], [Boehm 2008]].--><p>然而，仅靠它还无法让我们同步多个常规变量的访问，或者是同步不同内存位置的 atomic 访问。为了支持这种同步功能，每一个 atomic 操作都需要能够配置成同线程里其它变量访问的同步节点，避免之前的变量访问在 atomic 操作后执行，诸如此类。另一个线程的 atomic 操作也可以与同一个节点进行同步，让多线程的访问建立起一条严格的（局部的）时间线。这样，我们就可以推导出多线程操作可能的顺序，尽管底层实现对于我们来说是不透明的（这就是锁和 DispatchQueue 用来序列化随机访问共享变量的方式），详细细节可以看 [<a href="https://isocpp.org/std/the-standard">C++17</a>, <a href="http://wg21.link/N2153">N2153</a>, <a href="https://doi.org/10.1145/1375581.1375591">Boehm 2008</a>]。</p><blockquote><p>这一段比较复杂，少了一些上下文讲解，其实就是 CPU 为了最大化执行效率会乱序执行我们的代码，只保证最终的运行效果，这个过程中需要我们提供一些额外的信息，让 CPU 知道线程间共享的数据以及它们共享的方式。</p><p>推荐阅读：</p><ul><li><a href="http://wilburding.github.io/blog/2013/04/07/c-plus-plus-11-atomic-and-memory-model/">什么是 Memory Ordering？</a></li><li><a href="https://www.zhihu.com/question/24301047/answer/85844428">如何理解 C++11 的六种 memory order？</a></li></ul><p>另外这里也可以参考原文：</p><p>However, this alone does not give us a way to synchronize accesses to regular variables, or between atomic accesses to different memory locations. To support such synchronization, each atomic operation can be configured to also act as a synchronization point for other variable accesses within the same thread, preventing previous accesses from getting executed after the atomic operation, and&#x2F;or vice versa. Atomic operations on another thread can then synchronize with the same point, establishing a strict (although partial) timeline between accesses performed by both threads. This way, we can reason about the possible ordering of operations across threads, even if we know nothing about how those operations are implemented. (This is how locks or dispatch queues can be used to serialize the execution of arbitrary blocks containing regular accesses to shared variables.) For more details, see [<a href="https://isocpp.org/std/the-standard">C++17</a>, <a href="http://wg21.link/N2153">N2153</a>, <a href="https://doi.org/10.1145/1375581.1375591">Boehm 2008</a>].</p></blockquote><!--We can use the the `ordering:` parameter of each atomic operation to specify the level of synchronization it needs to provide. This proposal introduces five distinct memory orderings, organized into three logical groups, from loosest to strictest:--><p>我们可以使用 <code>ordering:</code> 参数来指定每一个 atomic 操作的同步级别。这个提案会引入五个不同的 memory orderings，它们可以根据逻辑分为三个类别，从最宽松到最严格：</p><ul><li><code>.relaxed</code></li><li><code>.acquiring</code>, <code>.releasing</code>, <code>.acquiringAndReleasing</code></li><li><code>.sequentiallyConsistent</code></li></ul><!--These align with select members of the standard `std::memory_order` enumeration in C++, and are intended to carry the same semantic meaning:--><p>这与 C++ 标准库的 <code>std::memory_order</code> 相对应，并且它们的语义也是一致的：</p><table><thead><tr><th align="center">C++</th><th align="center">Swift</th></tr></thead><tbody><tr><td align="center"><code>std::memory_order_relaxed</code></td><td align="center"><code>.relaxed</code></td></tr><tr><td align="center"><code>std::memory_order_consume</code></td><td align="center"><em>not adopted yet</em> [<a href="http://wg21.link/P0735">P0735</a>]</td></tr><tr><td align="center"><code>std::memory_order_acquire</code></td><td align="center"><code>.acquiring</code></td></tr><tr><td align="center"><code>std::memory_order_release</code></td><td align="center"><code>.releasing</code></td></tr><tr><td align="center"><code>std::memory_order_acq_rel</code></td><td align="center"><code>.acquiringAndReleasing</code></td></tr><tr><td align="center"><code>std::memory_order_seq_cst</code></td><td align="center"><code>.sequentiallyConsistent</code></td></tr></tbody></table><!--We consider these ordering arguments to be an essential part of low-level atomic operations, and we require an explicit `ordering` argument on all atomic operations provided by `UnsafeAtomic`. The intention here is to force developers to carefully think about what ordering they need to use, each time they use one of these primitives. (Perhaps more importantly, this also makes it obvious to readers of the code what ordering is used -- making it far less likely that an unintended `.sequentiallyConsistent` ordering slips through code review.) --><p>我们认为这些 ordering 参数属于底层 atomic 操作的必要组成部分，所以我们要求 <code>UnsafeAtomic</code> 的所有 atomic 操作都必须提供 <code>ordering</code> 参数。这里的意图是强制开发者去谨慎思考每个 atomic 操作该使用的 ordering。（也许更重要的是，让阅读代码的人能够注意到这里使用的 ordering —— 让 <code>.sequentiallyConsistent</code> 在意图不明确时没那么容易通过 code review）</p><!--Projects that prefer to default to sequentially consistent ordering are welcome to add non-public `UnsafeAtomic` extensions that implement that. However, we expect that providing an implicit default ordering would be highly undesirable in most production uses of atomics.--><p>倾向于默认使用 <code>sequentiallyConsistent</code> ordering 的项目也可以给 <code>UnsafeAtomic</code> 添加一个 non-public 的 extension 去实现它。但我们还是认为 Atomics 在使用时不应该提供默认的 ordering。</p><!--Atomic orderings are grouped into three frozen structs based on the kind of operation to which they are attached, as listed below. By modeling these as separate types, we can ensure that unsupported operation/ordering combinations (such as an atomic "releasing load") will lead to clear compile-time errors:--><p>Atomic orderings 根据操作类型分为三个结构体，就像下面列出来的那样。通过把它们建模成不同的类型，我们可以保证不支持的操作&#x2F;ordering 会抛出明确的编译期错误：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@frozen</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">AtomicLoadOrdering</span> &#123;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> relaxed: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> acquiring: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> sequentiallyConsistent: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@frozen</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">AtomicStoreOrdering</span> &#123;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> relaxed: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> releasing: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> sequentiallyConsistent: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@frozen</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">AtomicUpdateOrdering</span> &#123;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> relaxed: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> acquiring: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> releasing: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> acquiringAndReleasing: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">var</span> sequentiallyConsistent: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--These structs behave like non-frozen enums with a known (non-public) raw representation. This allows us to define additional memory orderings in the future (if and when they become necessary) while making use of the known representation to optimize existing cases. (These cannot be frozen enums because that would prevent us from adding more orderings, but regular resilient enums can't freeze their representation, and the layout indirection interferes with guaranteed optimizations, especially in -Onone.)--><p>这些结构体表现得像是 non-frozen 的 enum，并且使用（non-public）已知的原始值展现形式。这让它们可以得到充分的优化，并且在未来也可以随时添加新的 memory orderings（必要时）。（它们不能是 frozon 的 enum，因为这样会让我们无法添加的 ordering，并且这样生成的内存布局也无法获得优化，特别是在 -Onone 时）</p><!--We also provide a top-level function called `atomicMemoryFence` that allows issuing a memory ordering constraint without directly associating it with a particular atomic operation. This corresponds to `std::memory_thread_fence` in C++ [[C++17]].--><p>我们也有一个全局函数 <code>atomicMemoryFence</code> 去提供 memory ordering 约束，而无需关联到特定的 atomic 操作。它与 C++ [<a href="https://isocpp.org/std/the-standard">C++17</a>] 的 <code>std::memory_thread_fence</code> 相对应。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">atomicMemoryFence</span>(<span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span>)</span><br></pre></td></tr></table></figure><!--Fences are slightly more powerful (but even more difficult to use) than orderings tied to specific atomic operations [[N2153]]; we expect their use will be limited to the most performance-sensitive synchronization constructs.--><p>Fences 比绑定到特定 atomic 操作的 Ordering 稍微更强力一些 [<a href="http://wg21.link/N2153">N2153</a>]（但也更难使用）；我们期望它们的使用只限于性能最敏感的使用场景。</p><h3 id="Atomic-协议的继承"><a href="#Atomic-协议的继承" class="headerlink" title="Atomic 协议的继承"></a>Atomic 协议的继承</h3><!--The notion of an atomic type is captured by the `AtomicProtocol` protocol. `AtomicInteger` refines it to add support for a select list of atomic integer operations.--><p>atomic 类型的概念会通过 <code>AtomicProtocol</code> 表达出来。<code>AtomicInteger</code> 将它提炼出来并且添加了一系列整型操作的支持。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">AtomicProtocol</span> &#123;</span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">AtomicInteger</span>: <span class="title class_ inherited__">AtomicProtocol</span>, <span class="title class_ inherited__">FixedWidthInteger</span></span><br><span class="line"><span class="keyword">where</span> ... &#123;</span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--While `AtomicProtocol` and `AtomicInteger` are public protocols, their requirements are considered an implementation detail of the Standard Library. (They are replaced by ellipses above.) --><p>虽然 <code>AtomicProtocol</code> 和 <code>AtomicInteger</code> 是公开协议，但它们的 requirements 会被看作是标准库的实现细节隐藏起来。（也就是上面省略掉的部分）</p><!--These hidden requirements set up a bidirectional mapping between values of the atomic type and an associated (private) storage representation that implements the actual primitive atomic operations. --><p>这些隐藏的 requirements 会给 atomic 类型的值和关联的存储形式设置了一个双向映射，由存储形式负责去实现原始的 atomic 操作。</p><!--The specific details are outside the scope of the Swift Evolution process and they are subject to arbitrarily change between Standard Library releases, as long as ABI compatibility is maintained (as necessary).--><p>这些特定的细节不会纳入到 Swift Evolution 的过程中，不同版本的标准库可能会有不同的实现，只要能够保持 ABI 兼容性。</p><!--Following existing Standard Library conventions for such interfaces, the names of all associated types and member requirements of these protocols start with a leading underscore character. As with any other underscored interface exposed by the Standard Library, code that manually implements or directly uses these underscored requirements may fail to compile (or correctly run) when built using any Swift release other than the one for which it was initially written. --><p>根据现有的标准库 API 设计习惯，这些协议的相关类型和成员都会使用下划线作为前缀。就像标准库里其它下划线开头的公开 API，手动实现或者访问这些下划线的属性和方法都有可能会导致编译或者运行错误。（when built using any Swift release other than the one for which it was initially written）</p><!--The full set of standard types implementing `AtomicProtocol` is listed below.--><p>下面列举的就是标准库里实现了 <code>AtomicProtocol</code> 的所有类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeRawPointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeMutableRawPointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafePointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeMutablePointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Unmanaged</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int64</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int32</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int16</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int8</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt64</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt32</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt16</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt8</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Optional</span>: <span class="title class_ inherited__">AtomicProtocol</span> <span class="keyword">where</span> <span class="title class_ inherited__">Wrapped</span>: <span class="title class_ inherited__">AtomicProtocol</span>, ... &#123;<span class="operator">...</span>&#125;</span><br></pre></td></tr></table></figure><!--We only provide atomic arithmetic operations on integer types. While it would be technically possible to allow atomic pointer arithmetic, this would be inherently unsafe, unless it is integrated with explicit checks to prevent the pointer value from escaping the extents of the underlying buffer. We do not consider such operations to be useful enough to include in the Standard Library; and the compare-exchange loop that implements them can be easily provided in user code as desired.--><p>我们只给整型类型提供 atomic 算数操作。虽然理论上我们可以让 atomic 指针也支持算数操作，但这本身就不是安全的做法，除非接入显式的指针逃逸检查。我们也不认为这种操作足够实用，以至于需要纳入进标准库里；并且用户可以自己通过 compare-exchange 循环很便捷地实现。</p><h4 id="Optional-Atomics"><a href="#Optional-Atomics" class="headerlink" title="Optional Atomics"></a>Optional Atomics</h4><!--The standard atomic pointer types and unmanaged references also support atomic operations on their optional-wrapped form. `Optional` implements this through a conditional conformance to `AtomicProtocol`; the exact constraint is an implementation detail. (It works by requiring the wrapped type's internal atomic storage representation to support a special nil value.)--><p>标准库的 atomic 指针类型和 unmanaged 引用也支持在它们的 optional-wrapped 类型上使用 atomic 操作。<code>Optional</code> 通过 <code>AtomicProtocol</code> 的 Conditional Conformance 实现这一点；实际的约束是一个实现细节（它会要求封装类型内部的 atomic 存储格式支持 nil 值）</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Optional</span>: <span class="title class_ inherited__">AtomicProtocol</span> <span class="keyword">where</span> ... &#123;</span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This proposal enables optional-atomics support for the following types:--><p>这个提案会为下列类型提供 optional-atomics 的支持：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="type">UnsafeRawPointer</span></span><br><span class="line"><span class="type">UnsafeMutableRawPointer</span></span><br><span class="line"><span class="type">UnsafePointer</span>&lt;<span class="type">Pointee</span>&gt;</span><br><span class="line"><span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Pointee</span>&gt;</span><br><span class="line"><span class="type">Unmanaged</span>&lt;<span class="type">Instance</span>&gt;</span><br></pre></td></tr></table></figure><!--User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals.--><p>用户无法通过代码手动实现和扩充这个列表，只能通过后续的提案去做这件事情。</p><!--Atomic optional pointers and references are helpful when building lock-free data structures. (Although this initial set of reference types considerably limits the scope of what can be built; for more details, see the discussion on the ABA problem and memory reclamation in the [Potential Future Directions](#double-wide-atomics-and-the-aba-problem) section.)--><p>在构建 lock-free 的数据结构时，Atomic optional 指针和引用非常有用。（尽管这些初始的引用类型限制了它能够搭建的东西；更多细节，请看关于 ABA 问题和<a href="#%E6%9C%AA%E6%9D%A5%E7%9A%84%E6%96%B9%E5%90%91">未来的方向</a>小节里提到的内存回收问题）</p><!--For example, consider the lock-free, single-consumer stack implementation below. (It supports an arbitrary number of concurrently pushing threads, but it only allows a single pop at a time.)--><p>举个例子，请看下面一个 lock-free，单一消费者的栈实现。（它支持随机并发多线程插入，但只允许每次 pop 一个）</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">LockFreeSingleConsumerStack</span>&lt;<span class="type">Element</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">Node</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> value: <span class="type">Element</span></span><br><span class="line">    <span class="keyword">var</span> next: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Node</span>&gt;?</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">typealias</span> <span class="type">NodePtr</span> <span class="operator">=</span> <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Node</span>&gt;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">var</span> _last <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">NodePtr</span>?&gt;.create(initialValue: <span class="literal">nil</span>)</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">var</span> _consumerCount <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">deinit</span> &#123;</span><br><span class="line">    <span class="comment">// Discard remaining nodes</span></span><br><span class="line">    <span class="comment">// 丢弃掉剩余的 nodes</span></span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">let</span> <span class="keyword">_</span> <span class="operator">=</span> pop() &#123;&#125;</span><br><span class="line">    _last.destroy()</span><br><span class="line">    _consumerCount.destroy()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Push the given element to the top of the stack.</span></span><br><span class="line">  <span class="comment">// It is okay to concurrently call this in an arbitrary number of threads.</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">push</span>(<span class="keyword">_</span> <span class="params">value</span>: <span class="type">Element</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> new <span class="operator">=</span> <span class="type">NodePtr</span>.allocate(capacity: <span class="number">1</span>)</span><br><span class="line">    new.initialize(to: <span class="type">Node</span>(value: value, next: <span class="literal">nil</span>))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> done <span class="operator">=</span> <span class="literal">false</span></span><br><span class="line">    <span class="keyword">var</span> current <span class="operator">=</span> _last.load(ordering: .relaxed)</span><br><span class="line">    <span class="keyword">while</span> <span class="operator">!</span>done &#123;</span><br><span class="line">      new.pointee.next <span class="operator">=</span> current</span><br><span class="line">      (done, current) <span class="operator">=</span> _last.compareExchange(</span><br><span class="line">        expected: current,</span><br><span class="line">        desired: new,</span><br><span class="line">        ordering: .releasing)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Pop and return the topmost element from the stack.</span></span><br><span class="line">  <span class="comment">// This method does not support multiple overlapping concurrent calls.</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">pop</span>() -&gt; <span class="type">Element</span>? &#123;</span><br><span class="line">    <span class="built_in">precondition</span>(</span><br><span class="line">      _consumerCount.loadThenWrappingIncrement(ordering: .acquiring) <span class="operator">==</span> <span class="number">0</span>,</span><br><span class="line">      <span class="string">&quot;Multiple consumers detected&quot;</span>)</span><br><span class="line">    <span class="keyword">defer</span> &#123; _consumerCount.wrappingDecrement(ordering: .releasing) &#125;</span><br><span class="line">    <span class="keyword">var</span> done <span class="operator">=</span> <span class="literal">false</span></span><br><span class="line">    <span class="keyword">var</span> current <span class="operator">=</span> _last.load(ordering: .acquiring)</span><br><span class="line">    <span class="keyword">while</span> <span class="keyword">let</span> c <span class="operator">=</span> current &#123;</span><br><span class="line">      (done, current) <span class="operator">=</span> _last.compareExchange(</span><br><span class="line">        expected: c,</span><br><span class="line">        desired: c.pointee.next,</span><br><span class="line">        ordering: .acquiring)</span><br><span class="line">      <span class="keyword">if</span> done &#123;</span><br><span class="line">        <span class="keyword">let</span> result <span class="operator">=</span> c.move()</span><br><span class="line">        c.deallocate()</span><br><span class="line">        <span class="keyword">return</span> result.value</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="自定义-Atomic-类型"><a href="#自定义-Atomic-类型" class="headerlink" title="自定义 Atomic 类型"></a>自定义 Atomic 类型</h4><!--To enable a limited set of user-defined atomic types, `AtomicProtocol` also provides a full set of default implementations for `RawRepresentable` types whose raw value is itself atomic:--><p>为了支持用户自定义 atomic 类型，<code>AtomicProtocol</code> 为 <code>RawPresentable</code> 提供了一系列完整的默认实现，只要它的 rawValue 也是 atomic 的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">AtomicProtocol</span> </span><br><span class="line"><span class="keyword">where</span> <span class="keyword">Self</span>: <span class="type">RawRepresentable</span>, <span class="type">RawValue</span>: <span class="type">AtomicProtocol</span>, <span class="operator">...</span> &#123;</span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The omitted constraint sets up the (hidden) atomic storage type to match that of the `RawValue`. The default implementations work by forwarding all atomic operations to the raw value's implementation, converting to/from as needed.--><p>被省略的约束设置了一个与 <code>RawValue</code> 相匹配（隐藏的）atomic 存储类型。默认的实现会转发所有 atomic 操作给 rawValue 的实现，根据需要转化 to&#x2F;from。</p><!--This enables code outside of the Standard Library to add new `AtomicProtocol` conformances without manually implementing any of the hidden requirements. This is especially handy for trivial raw-representable enumerations, such as in simple atomic state machines:--><p>这样让标准库以外的代码也可以添加新的 <code>AtomicProtocol</code> conformance，并且不需要手动实现所有隐藏的实现要求。在实现一些简单的 raw-represenable 的枚举时特别实用，例如简单的 atomic 状态机：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">MyState</span>: <span class="title class_ inherited__">Int</span>, <span class="title class_ inherited__">AtomicProtocol</span> &#123;</span><br><span class="line">  <span class="keyword">case</span> starting</span><br><span class="line">  <span class="keyword">case</span> running</span><br><span class="line">  <span class="keyword">case</span> stopped</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> currentState <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">MyState</span>&gt;.create(initialValue: .starting)</span><br><span class="line"><span class="operator">...</span></span><br><span class="line"><span class="keyword">if</span> currentState.compareExchange(</span><br><span class="line">    expected: .starting, </span><br><span class="line">    desired: .running, </span><br><span class="line">    ordering: .sequentiallyConsistent</span><br><span class="line">  ).exchanged &#123;</span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="operator">...</span></span><br><span class="line">currentState.store(.stopped, ordering: .sequentiallyConsistent)</span><br><span class="line"><span class="operator">...</span></span><br><span class="line">currentState.destroy()</span><br></pre></td></tr></table></figure><h3 id="将-Ordering-参数约束为编译期可推导的常量"><a href="#将-Ordering-参数约束为编译期可推导的常量" class="headerlink" title="将 Ordering 参数约束为编译期可推导的常量"></a>将 Ordering 参数约束为编译期可推导的常量</h3><!--Modeling orderings as regular function parameters allows us to specify them using syntax that's familiar to all Swift programmers. Unfortunately, it means that in the implementation of atomic operations we're forced to switch over the ordering argument:--><p>把 ordering 建模为普通的函数参数可以让我们使用所有 Swift 程序员都熟悉的语法。不幸的是，这意味着 atomic 操作的实现里我们必须使用 switch 匹配 ordering 参数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Int</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">typealias</span> <span class="type">AtomicStorage</span> <span class="operator">=</span> <span class="keyword">Self</span></span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">atomicCompareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Int</span>,</span><br><span class="line">    <span class="params">desired</span>: <span class="type">Int</span>,</span><br><span class="line">    <span class="params">at</span> <span class="params">address</span>: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">AtomicStorage</span>&gt;,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Int</span>) &#123;</span><br><span class="line">    <span class="comment">// Note: This is a simplified version of the actual implementation</span></span><br><span class="line">    <span class="keyword">let</span> won: <span class="type">Bool</span></span><br><span class="line">    <span class="keyword">let</span> oldValue: <span class="type">Int</span></span><br><span class="line">    <span class="keyword">switch</span> ordering &#123;</span><br><span class="line">    <span class="keyword">case</span> .relaxed:</span><br><span class="line">      (oldValue, won) <span class="operator">=</span> <span class="type">Builtin</span>.cmpxchg_monotonic_monotonic_Word(</span><br><span class="line">        address, expected, desired)</span><br><span class="line">    <span class="keyword">case</span> .acquiring:</span><br><span class="line">      (oldValue, won) <span class="operator">=</span> <span class="type">Builtin</span>.cmpxchg_acquire_acquire_Word(</span><br><span class="line">        address, expected, desired)</span><br><span class="line">    <span class="keyword">case</span> .releasing:</span><br><span class="line">      (oldValue, won) <span class="operator">=</span> <span class="type">Builtin</span>.cmpxchg_release_monotonic_Word(</span><br><span class="line">        address, expected, desired)</span><br><span class="line">    <span class="keyword">case</span> .acquiringAndReleasing:</span><br><span class="line">      (oldValue, won) <span class="operator">=</span> <span class="type">Builtin</span>.cmpxchg_acqrel_acquire_Word(</span><br><span class="line">        address, expected, desired)</span><br><span class="line">    <span class="keyword">default</span>: <span class="comment">// .sequentiallyConsistent</span></span><br><span class="line">      (oldValue, won) <span class="operator">=</span> <span class="type">Builtin</span>.cmpxchg_seqcst_seqcst_Word(</span><br><span class="line">        address, expected, desired)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (won, oldValue)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Given our requirement that primitive atomics must always compile down to the actual atomic instructions with minimal additional overhead, we must guarantee that these switch statements always get optimized away into the single case we need; they must never actually be evaluated at runtime.--><p>由于我们要求 atomic 原语必须以最少的代价编译到对应的 atomic 指令，我们必须保证 switch 语句总能优化成我们需要的单个 case；它们不能放到运行时去计算。</p><!--Luckily, configuring these special functions to always get force-inlined into all callers guarantees that constant folding will get rid of the switch statement *as long as the supplied ordering is a compile-time constant*. Unfortunately, it's all too easy to accidentally violate this latter requirement, with dire consequences to the expected performance of the atomic operation.--><p>幸运的是，这些特殊的函数可以通过配置，强制内联到每一个调用方里，保证让 constant folding 移除掉 switch 语句，<em>只要 ordering 是一个编译期的常数</em>。不幸的是，第二个要求很难达成，并且也没办法达到 atomic 操作期望的性能。</p><!--Consider the following well-meaning attempt at using `compareExchange` to define an atomic integer addition operation that traps on overflow rather than allowing the result to wrap around:--><p>假设我们要使用 <code>compareExchange</code> 来定义一个 atomic 整型的加法运算，会在溢出时触发 trap，而不是产生任何未定义行为：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> <span class="keyword">where</span> <span class="type">Value</span> <span class="operator">==</span> <span class="type">Int</span> &#123;</span><br><span class="line">  <span class="comment">// Non-inlinable</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">checkedIncrement</span>(<span class="params">by</span> <span class="params">delta</span>: <span class="type">Int</span>, <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span>) &#123;</span><br><span class="line">    <span class="keyword">var</span> done <span class="operator">=</span> <span class="literal">false</span></span><br><span class="line">    <span class="keyword">var</span> current <span class="operator">=</span> load(ordering: .relaxed)</span><br><span class="line">    <span class="keyword">while</span> <span class="operator">!</span>done &#123;</span><br><span class="line">      (done, current) <span class="operator">=</span> compareExchange(</span><br><span class="line">        expected: current,</span><br><span class="line">        desired: current <span class="operator">+</span> operand, <span class="comment">// Traps on overflow</span></span><br><span class="line">        ordering: ordering)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Elsewhere:</span></span><br><span class="line">counter.checkedIncrement(by: <span class="number">1</span>, ordering: .relaxed)</span><br></pre></td></tr></table></figure><!--If for whatever reason the Swift compiler isn't able (or willing) to inline the `checkedIncrement` call, then the value of `ordering` won't be known at compile time to the body of the function, so even though `compareExchange` will still get inlined, its switch statement won't be eliminated. This leads to a potentially significant performance regression that could interfere with the scalability of the operation.--><p>如果 Swift 编译器出于某些原因无法内联 <code>checkedIncrement</code> 的调用，那么 <code>ordering</code> 的值就无法在编译期推断出来，即使 <code>compareExchange</code> 会被内联，它的 switch 语句也无法省略。这会导致潜在的性能严重劣化，影响操作的可伸缩性（scalability）。</p><!--To prevent these issues, we are adding a special type checking phase that artificially constrains the memory ordering arguments of all atomic operations to compile-time constants. Any attempt to pass a dynamic ordering value (such as in the `compareExchange` call above) will result in a compile-time error.--><p>为了避免这些问题，我们会添加一个特殊的类型检查阶段，规定所有的 atomic 操作的 ordering 参数都必须为编译器常量。任何试图传递动态 ordering 值的代码都会产生编译错误（例如上面的 <code>compareExchange</code> 调用）</p><!--An ordering expression will be considered constant-evaluable if it's either (1) a direct call to one of the `Atomic*Ordering` factory methods (`.relaxed`, `.acquiring`, etc.), or (2) it is a direct reference to a variable that is in turn constrained to be constant-evaluable.--><p>一个 ordering 表达式想要在编译期优化为常数需要满足（1）直接调用 <code>Atomic*Ordering</code> 的工厂方法（<code>.relaxed</code>, <code>.acquiring</code>, 等等），或者（2）它引用了可计算的常量。</p><!--> **Note:** The implementation of this feature is available in a separate PR, [apple/swift#26969][constantPR].--><blockquote><p><strong>注意</strong> 这个功能的实现在另外一个的 PR 里 <a href="https://github.com/apple/swift/pull/26969">apple&#x2F;swift#26969</a>。</p></blockquote><!--The compiler work to make this happen could eventually form the basis of a new general-purpose language facility around constant-evaluable expressions; however, the initial implementation only supports the specific set of atomic operations introduced in this proposal. (For now, user-defined wrappers like `checkedIncrement` above won't be able to take an ordering parameter and pass it to an underlying atomic operation.)--><p>实现这个功能相关的编译器改动最终可以变成一个通用功能，围绕着表达式常量化展开（constant-evluable expressions）；然而，第一版的实现只是为了支持这个计划里引入的 atomic 操作。（目前，用户自定义的封装作为 ordering 参数传递给底层的 atomic 操作，例如 <code>checkedIncrement</code>）</p><h2 id="与现有的语言功能交互"><a href="#与现有的语言功能交互" class="headerlink" title="与现有的语言功能交互"></a>与现有的语言功能交互</h2><h3 id="独占性原则的修正"><a href="#独占性原则的修正" class="headerlink" title="独占性原则的修正"></a>独占性原则的修正</h3><!--The new atomic operations appear to implement read or write access to some sort of variable, but unlike regular read/write accesses, it is inherently safe to execute them concurrently. Indeed, allowing concurrent access is the primary reason we want to introduce them! Therefore, we must make sure that the Law of Exclusivity won't disallow such use.--><p>新的 atomic 操作看起来就是变量的读或写访问，但与常规的读&#x2F;写访问不同的是，并发地执行它们是安全的。实际上，安全的并发访问就是我们引入的原因！所以我们必须保证独占性原则不会禁止这种用例。</p><!--The proposed atomic operations are implemented as unsafe pointer operations; in fact, the new atomic types are merely thin wrappers around unsafe pointers. While [[SE-0176]] didn't introduce any active enforcement of the Law of Exclusivity for unsafe pointers, it still defined overlapping read/write access to their pointee as an exclusivity violation.--><p>提案里的 atomic 操作会实现为 unsafe 指针操作；实际上，新的 atomic 类型基本上就是 unsafe 指针的一层薄薄的封装。虽然 [<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md">SE-0176</a>] 的独占性原则没有涉及 unsafe 指针，但它还是将 pointee 重叠的读写访问判定为违反独占性原则。</p><!--To resolve this problem, we propose to introduce the concept of *atomic access*, and to amend the Law of Exclusivity as follows:--><p>为了解决这个问题，我们提议引入 <strong>atomic 访问</strong>的概念，并且将独占性访问原则修正为：</p><!--> Two accesses to the same variable aren't allowed to overlap unless both accesses are reads **or both accesses are atomic**.--> <blockquote><p>同一变量的两个访问不能重叠，除非它们都是读访问<strong>或者都是 atomic 访问</strong>。</p></blockquote><!--We define *atomic access* as a call to one of the atomic operations introduced in this proposal: `load(ordering:)`, `compareExchange(expected:desired:ordering:)`, etc. We consider two of these operations to *access the same variable* if they operate on the same underlying memory location. (Future proposals may introduce additional ways to perform atomic access.)--><p>我们将<strong>atomic 访问</strong>解释为这个提案里引入的 atomic 操作：<code>load(ordering:)</code>, <code>compareExchange(expected:desired:ordering:)</code>, 等等。如果两个访问了相同内存地址的操作属于上述的 atomic 操作，那我们就认为是在它们是在<strong>访问同一个变量</strong>。（未来的提案可能会引入更多 atomic 访问的方式）</p><!--We view the amendment above as merely formalizing pre-existing practice, rather than introducing any actual new constraint. --><p>我们认为上述的修正只是换了个方式重新阐述之前已有的内容，而并不是引入新的约束。</p><!--> **Note:** As such, this proposal is mostly about a library-level addition; its implementation doesn't need to change how the Swift compiler implements the Swift memory model. For example, there is no need to relax any existing compile-time or runtime checks for exclusivity violations, because unsafe pointer operations aren't currently covered by such checks. Similarly, because the new operations map directly to llvm's atomic instructions, they smoothly interoperate with the existing llvm-based Thread Sanitizer tool [[Tsan1], [TSan2]].--><blockquote><p><strong>注意</strong>，类似于这样，这个提案里绝大部分都是在 library 层面做加法；它的实现不需要改变 Swift 编译器实现 Swift 内存模型的方式。例如，不需要减少任何现有编译时&#x2F;运行时的独占性检查，因为 unsafe 指针操作不在检查的范围内。同样的，因为新的操作会直接映射为 LLVM 的 atoimc 指令，它们可以与现有的 LLVM Thread Sanitizer 工具无缝兼容 [<a href="https://developer.apple.com/documentation/code_diagnostics/thread_sanitizer">Tsan1</a>, <a href="https://clang.llvm.org/docs/ThreadSanitizer.html">TSan2</a>]）。</p></blockquote><!--For now, we leave mixed atomic/non-atomic access to the same memory location as undefined behavior, even if the mixed accesses are guaranteed to never overlap. (This restriction does not apply to accesses during storage initialization and deinitialization; those are always nonatomic.) A future proposal may lift this limitation.--><p>目前，我们暂时将混合使用 atomic&#x2F;non-atomic 访问同一内存位置的行为看作是未定义行为，即便混合访问能够保证不会重叠（这个限制不会作用于存储空间初始化和销毁期间的访问，它们总是 nonatomic 的）。可能未来的提案会放宽这个限制。</p><h3 id="非瞬时访问"><a href="#非瞬时访问" class="headerlink" title="非瞬时访问"></a>非瞬时访问</h3><!--Note: This section merely highlights a preexisting consequence of the Law of Exclusivity. It doesn't propose any changes to the language or the Standard Library.--><p>注意：这个小节只是再强调一遍独占性原则带来的连锁反应，它不是在提议要做任何语言或者标准库的改动。</p><!--As described in [[SE-0176]], Swift allows accesses that are non-instantaneous. For example, calling a `mutating` method on a variable counts as a single write access that is active for the entire duration of the method call:--><p>就像 [<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md">SE-0176</a>] 里阐述的那样，Swift 允许非瞬时访问（non-instantaneous）。举个例子，调用变量的 <code>mutating</code> 方法会被视为函数调用期间活跃的读写访问：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> integers: [<span class="type">Int</span>] <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="operator">...</span></span><br><span class="line">integers.sort() <span class="comment">// A single, long write access</span></span><br></pre></td></tr></table></figure><!--The Law of Exclusivity disallows overlapping read/write and write/write accesses to the same variable, so while one thread is performing `sort()`, no other thread is allowed to access `integers` at all. Note that this is independent of `sort()`'s implementation; it is merely a consequence of the fact that it is declared `mutating`.--><p>独占性原则禁止同一变量同时发生的<strong>读&#x2F;写</strong>和<strong>写&#x2F;写</strong>访问，所以当一个线程调用 <code>sort()</code> 时，其它的线程都不允许访问 <code>integers</code>。注意这与 <code>sort()</code> 的实现无关，只是声明为 <code>mutating</code> 产生的连锁效应。</p><!--> **Note:** One reason for this is that the compiler may decide to implement the mutating call by first copying the current value of `integers` into a temporary variable, running `sort` on that, and then copying the resulting value back to `integers`. If `integers` had a computed getter and setter, this is in fact the only reasonable way to implement the mutating call. If overlapping access wasn't disallowed, such implicit copying would lead to race conditions even if the `mutating` method did not actually mutate any data at all.--><blockquote><p>**注意：**这么做的理由是，编译器也许会调整 mutating 的实现，先将当前值拷贝到一个临时变量，运行完 <code>sort</code> 之后，再复制回原本的 <code>integers</code>。如果 <code>integers</code> 是一个计算属性，这是唯一一个合理实现 mutating 调用的方式。如果没有禁止重叠访问，这种隐式拷贝也许会导致竞态问题，即便 mutating 方法的实现里没有实际改变任何数据。</p></blockquote><!--An important aspect of atomic memory orderings is that they can only synchronize accesses whose duration doesn't overlap with the atomic operations themselves. They inherently cannot synchronize variable accesses that are still in progress while the atomic operation is being executed.--><p>atomic memory orderings 重要的一点是，它们在 atomic 操作期间只可以进行同步访问，并且不会重叠。在其它 atomic 操作执行期间它们无法同步访问变量。</p><!--This means that it isn't possible to implement any "thread-safe" `mutating` methods, no matter how much synchronization we add to their implementation. For example, the following attempt to implement an "atomic" increment operation on `Int` is inherently doomed to failure:--><p>这意味着不可能实现任何“线程安全”的 <code>mutating</code> 方法，无论我们在它们的实现里有多少同步操作。例如，下面尝试给 <code>Int</code> 实现一个 “atomic” 的加法操作，它注定失败：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> Dispatch</span><br><span class="line"><span class="keyword">import</span> Foundation</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> _mutex <span class="operator">=</span> <span class="type">NSLock</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int</span> &#123;</span><br><span class="line">  <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">atomicIncrement</span>() &#123; <span class="comment">// BROKEN, DO NOT USE</span></span><br><span class="line">    _mutex.lock()</span><br><span class="line">    <span class="keyword">self</span> <span class="operator">+=</span> <span class="number">1</span></span><br><span class="line">    _mutex.unlock()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> i: <span class="type">Int</span></span><br><span class="line"><span class="operator">...</span></span><br><span class="line">i <span class="operator">=</span> <span class="number">0</span></span><br><span class="line"><span class="type">DispatchQueue</span>.concurrentPerform(iterations: <span class="number">10</span>) &#123; <span class="keyword">_</span> <span class="keyword">in</span></span><br><span class="line">  <span class="keyword">for</span> <span class="keyword">_</span> <span class="keyword">in</span> <span class="number">0</span> <span class="operator">..&lt;</span> <span class="number">1_000_000</span> &#123;</span><br><span class="line">    i.atomicIncrement()  <span class="comment">// Exclusivity violation</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">print</span>(i)</span><br></pre></td></tr></table></figure><!--Even though `NSLock` does guarantee that the `self += 1` line is always serialized, the concurrent `atomicIncrement` invocations still count as an exclusivity violation, because the write access to `i` starts when the function call begins, before the call to `_mutex.lock()`. Therefore, the code above has undefined behavior, despite all the locking. (For example, it may print any value between one and ten million, or it may trap in a runtime exclusivity check, or indeed it may do something else.)--><p>虽然 <code>NSLock</code> 能够保证 <code>self += 1</code> 这一行总是顺序执行，并发的 <code>atomicIncrement</code> 调用还是被判定为违反了独占性原则，因为 <code>i</code> 的写访问在函数调用时就开始了，在 <code>_mutex.lock()</code> 的调用之前。所以，上面的代码存在未定义行为，尽管它们都加锁了。（举个例子，它也许会打印任何 1 到 10_000_000 之间的任何值，或者它会触发运行时的独占性检查，亦或者其它。）</p><!--Note that this restriction wasn't introduced by our new low-level atomic primitives -- it is a preexisting property of the language.--><p>注意这个约束不是新的 low-level atomic 原语引入的 —— 它之前就存在于语言里。</p><!--This is one of the reasons why `AtomicCounter` and `LockFreeSingleConsumerStack` were declared as classes above. Class instance methods are allowed to mutate their stored properties without declaring themselves `mutating`, and thus they are outside the scope of the Law of Exclusivity. (Of course, their implementation must still guarantee that the Law is upheld for any variables they access.)--><p>这也是前面提到的 <code>AtomicCounter</code> 和 <code>LockFreeSingleCosumerStack</code> 声明为 class 的其中一个原因。类实例方法允许改变它们的存储变量，无需声明为 <code>mutating</code>，因此它们并不在独占性原则的访问内。（当然，它们的实现必须保证它们访问的变量遵守这个原则）</p><!--> **Note:** A more fundamental reason why these constructs are classes is that synchronization constructs are difficult to model with value types -- their instances tend to have an inherent identity that prevents copies from working like the original, they often need to be backed by a stable memory location, etc. The [Ownership Manifesto]'s non-copiable types may eventually provide a more efficient and safer model for such constructs, but in today's Swift, we need to represent them with some reference type instead: typically, either a class (like `AtomicCounter`) or some unsafe pointer type (like `UnsafeAtomic`).--> <blockquote><p>**注意：**这些结构被声明为 class 更本质的原因是它们难以使用 struct 进行建模 —— 它们的实例倾向于持有一个固定的标识去避免拷贝，它们经常需要使用稳定的内存空间，诸如此类。<a href="https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md">Ownership Manifesto</a> 的 non-copiable 类型也许最终会提供一个更高效和更安全的模型，但目前的 Swift 里我们需要某种引用类型来呈现它们：通常来说，是 class（像是 <code>AtomicCounter</code>）或者是一些 unsafe 指针类型（像是 <code>UnsafeAtomic</code>)</p></blockquote><h3 id="隐式指针转换"><a href="#隐式指针转换" class="headerlink" title="隐式指针转换"></a>隐式指针转换</h3><!--To simplify interoperability with functions imported from C, Swift provides several forms of implicit conversions from Swift values to unsafe pointers. This often requires the use of Swift's special `&` syntax for passing inout values. At first glance, this use of the ampersand resembles C's address-of operator, and it seems to work in a similar way:--><p>为了简化导入的 C 函数的交互，Swift 提供了几种隐式转换让 Swift 的值可以转换为 unsafe 指针。这通常需要使用 Swift 特殊的 <code>&amp;</code> 语法来传递 inout 值。乍一看这个符号跟 C 的取址语法很像，而且作用也一样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">a</span>(<span class="keyword">_</span> <span class="params">ptr</span>: <span class="type">UnsafePointer</span>&lt;<span class="type">CChar</span>&gt;)</span><br><span class="line"><span class="keyword">func</span> <span class="title function_">b</span>(<span class="keyword">_</span> <span class="params">ptr</span>: <span class="type">UnsafePointer</span>&lt;<span class="type">Int</span>&gt;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Implicit conversion from String to nul-terminated C string</span></span><br><span class="line">a(<span class="string">&quot;Hello&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Implicit conversion from Array to UnsafePointer&lt;Element&gt;</span></span><br><span class="line"><span class="keyword">var</span> array <span class="operator">=</span> [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">b(array)  <span class="comment">// passes a pointer to array&#x27;s underlying storage buffer</span></span><br><span class="line">b(<span class="operator">&amp;</span>array) <span class="comment">// another way to spell the same</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Implicit conversion from inout T to UnsafePointer&lt;T&gt;</span></span><br><span class="line"><span class="keyword">var</span> value <span class="operator">=</span> <span class="number">42</span></span><br><span class="line">b(<span class="operator">&amp;</span>value)</span><br><span class="line">b(<span class="operator">&amp;</span>array[<span class="number">0</span>])</span><br></pre></td></tr></table></figure><!--Unfortunately, Swift variables do not necessarily have a stable location in memory, and even in case they happen to get assigned one, there is generally no reliable way to retrieve the address of their storage. (The obvious exceptions are dynamic variables that we explicitly allocate ourselves.) --><p>不幸的是，Swift 的变量内存位置并不一定是固定的，即使是它们在被赋值时，也没有可靠的方式去获取它们存储的地址。（当然，我们显式地 allocate 时例外。）</p><!--While these conversions sometimes allow us to call C functions with less typing, they are extremely misleading -- to the point of being actively harmful. The problem is that unlike in C, the resulting pointers are only guaranteed to be valid for the duration of the function call. The pointer conversions above may (and frequently do!) create a temporary copy of the inout value that gets destroyed when the function returns. Holding onto the pointer after the function returns leads to undefined behavior. (Even if it appears to work in a particular situation, it may break the next time the code is recompiled with seemingly irrelevant changes.)--><p>尽管这些转换有时可以让我们调用 C 函数时少写一些代码，但它们也非常容易产生误导 —— 甚至造成损失。问题在于 Swift 跟 C 不一样，生成的指针只在函数调用期间保证有效。上面的指针的转换可能（也经常）会给 inout 值创建一个临时拷贝，并且在函数返回时销毁。在函数返回之后继续使用这些指针就会导致未定义行为。（即使特定情况下它似乎没有引起任何问题，但也许在一些不相关的代码修改后被破坏。）</p><!--For example, we may be tempted to eliminate a memory allocation for an `UnsafeAtomic` instance by using an inout-to-pointer conversion to "take the address of" a class instance variable, and passing it to the `UnsafeAtomic.init(at:)` initializer. This is not supported in the language, and it leads to undefined behavior.--><p>举个例子，我们也许想要减少 <code>UnsafeAtomic</code> 实例的内存分配，直接通过 &amp; 将一个实例转换为指针，然后将它传递给 <code>UnsafeAtomic.init(at:)</code> 构造器。但语言层面是不支持这么做的，并且会导致未定义行为。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BrokenAtomicCounter</span> &#123; <span class="comment">// THIS IS BROKEN; DO NOT USE</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">var</span> _storage <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;.<span class="type">Storage</span>(<span class="number">0</span>)</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">var</span> _value: <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;?</span><br><span class="line">      </span><br><span class="line">  <span class="keyword">init</span>() &#123;</span><br><span class="line">    <span class="comment">// This escapes the ephemeral pointer generated by the inout expression,</span></span><br><span class="line">    <span class="comment">// so it leads to undefined behavior when the pointer gets dereferenced</span></span><br><span class="line">    <span class="comment">// in the atomic operations below. DO NOT DO THIS.</span></span><br><span class="line">    _value <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;(at: <span class="operator">&amp;</span>_storage)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">increment</span>() &#123;</span><br><span class="line">    _value<span class="operator">!</span>.wrappingIncrement(by: <span class="number">1</span>, ordering: .relaxed)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">get</span>() -&gt; <span class="type">Int</span> &#123;</span><br><span class="line">    _value<span class="operator">!</span>.load(ordering: .relaxed)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--To prevent such misuse, in the current implementation of this proposal, the code above generates a warning:--><p>为了避免这种错误的用法，这个提案目前的实现里，这样的代码会生成一个警告：</p><figure class="highlight text"><table><tr><td class="code"><pre><span class="line">warning: inout expression creates a temporary pointer, but argument &#x27;at&#x27; should be </span><br><span class="line">a pointer that outlives the call to &#x27;init(at:)&#x27;</span><br><span class="line">    _value = UnsafeAtomic&lt;Int&gt;(at: &amp;_storage)</span><br><span class="line">                                   ^~~~~~~~~</span><br></pre></td></tr></table></figure><!--This is implemented using a preexisting diagnostic based on a compiler heuristic. Ideally this warning would be promoted to a compile-time error.--><p>这是借助一个已有 Diagnostic 机制实现的。理想情况下这个警告会升级为编译期错误。</p><!--> **Note:** For an idea on how to add proper language support for taking the address of certain kinds of variables, see the discussion on the hypothetical `@addressable` attribute in [Memory\-Safe Atomic Constructs](#memory-safe-atomic-constructs).--><blockquote><p><strong>注意</strong>：如何为变量取址提供语言支持，可以看 <a href="#%E5%86%85%E5%AD%98%E5%AE%89%E5%85%A8%E7%9A%84-Atomic-%E7%BB%93%E6%9E%84">内存安全的 Atomic 结构</a> 里虚构的 <code>@addressable</code>。</p></blockquote><h2 id="方案设计细节"><a href="#方案设计细节" class="headerlink" title="方案设计细节"></a>方案设计细节</h2><!--In the interest of keeping this document (relatively) short, the following API synopsis does not include API documentation, inlinable method bodies, or `@usableFromInline` declarations, and omits most attributes (`@available`, `@inlinable`, etc.).--><p>为了尽量让这份文档保持简洁，下面的 API 概要不包含文档，inlinable 的函数实现或者 <code>@usableFromInline</code> 的命名，并且省略掉大部分的注解（<code>@available</code>, <code>@inlinable</code> 等等）。</p><!--To allow atomic operations to compile down to their corresponding CPU instructions, most entry points listed here will be defined `@inlinable`.--><p>为了让 Atomic 操作能够编译到它对应的 CPU 指令，下面大部分罗列的入口点都会定义为 <code>@ininable</code>。</p><!--For the full API definition, please refer to the [implementation][implementation].--><p>完整的 API 定义请查看 <a href="https://github.com/apple/swift/pull/30553">implementation</a>。</p><h3 id="Atomic-Memory-Orderings-1"><a href="#Atomic-Memory-Orderings-1" class="headerlink" title="Atomic Memory Orderings"></a>Atomic Memory Orderings</h3><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">atomicMemoryFence</span>(<span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">@frozen</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">AtomicLoadOrdering</span>: <span class="title class_ inherited__">Equatable</span>, <span class="title class_ inherited__">Hashable</span>, <span class="title class_ inherited__">CustomStringConvertible</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> relaxed: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> acquiring: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> sequentiallyConsistent: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">==</span>(<span class="params">left</span>: <span class="keyword">Self</span>, <span class="params">right</span>: <span class="keyword">Self</span>) -&gt; <span class="type">Bool</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>)</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">var</span> description: <span class="type">String</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@frozen</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">AtomicStoreOrdering</span>: <span class="title class_ inherited__">Equatable</span>, <span class="title class_ inherited__">Hashable</span>, <span class="title class_ inherited__">CustomStringConvertible</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> relaxed: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> releasing: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> sequentiallyConsistent: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">==</span>(<span class="params">left</span>: <span class="keyword">Self</span>, <span class="params">right</span>: <span class="keyword">Self</span>) -&gt; <span class="type">Bool</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>)</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">var</span> description: <span class="type">String</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@frozen</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">AtomicUpdateOrdering</span>: <span class="title class_ inherited__">Equatable</span>, <span class="title class_ inherited__">Hashable</span>, <span class="title class_ inherited__">CustomStringConvertible</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> relaxed: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> acquiring: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> releasing: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> acquiringAndReleasing: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">var</span> sequentiallyConsistent: <span class="keyword">Self</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">==</span>(<span class="params">left</span>: <span class="keyword">Self</span>, <span class="params">right</span>: <span class="keyword">Self</span>) -&gt; <span class="type">Bool</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>)</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">var</span> description: <span class="type">String</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Atomic-协议"><a href="#Atomic-协议" class="headerlink" title="Atomic 协议"></a>Atomic 协议</h3><h4 id="protocol-AtomicProtocol"><a href="#protocol-AtomicProtocol" class="headerlink" title="protocol AtomicProtocol"></a><code>protocol AtomicProtocol</code></h4><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">AtomicProtocol</span> &#123;</span><br><span class="line">  <span class="comment">// Requirements aren&#x27;t public API.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The requirements set up a bidirectional mapping between values of the atomic type and an associated (private) storage representation that supplies the actual primitive atomic operations. --><p>这里的 requirements 会给 Atomic 类型的值和关联的 Storage 建立了双向映射，让 Storage 提供实际的原始的 atomic 操作。</p><!--The exact requirements are a private implementation detail of the Standard Library. They are outside the scope of the Swift Evolution process and they may arbitrarily change between library releases. User code must not directly use them or manually implement them.--><p>确切的 requirements 是标准库的私有实现细节。他们不在 Swift Evolution 的范围内，随着 Swift 版本迭代它们随时可能会被改变。用户不能手动通过代码直接使用或者实现它们。</p><!--Conforming types:--><p>遵循的类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeRawPointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeMutableRawPointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafePointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeMutablePointer</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Unmanaged</span>: <span class="title class_ inherited__">AtomicProtocol</span> &#123;<span class="operator">...</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Optional</span>: <span class="title class_ inherited__">AtomicProtocol</span> <span class="keyword">where</span> <span class="title class_ inherited__">Wrapped</span>: <span class="title class_ inherited__">AtomicProtocol</span>, ... &#123;<span class="operator">...</span>&#125;</span><br></pre></td></tr></table></figure><!--The exact constraints on `Optional`'s conditional conformance are a private implementation detail. (They specify that the underlying (private) storage representation must be able to represent an extra `nil` value.)--><p><code>Optional</code> 的 conditional conformance 确切的约束也是一个私有的实现细节。（它们指定了底层（私有）的存储形式，这种形式必须能够表现一个额外的 <code>nil</code> 值）</p><!--Atomic `Optional` operations are currently enabled for the following `Wrapped` types:--><p>当 <code>Wrapped</code> 为以下类型时，Atomic 的 <code>Optional</code> 才会提供 atomic 操作：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="type">UnsafeRawPointer</span></span><br><span class="line"><span class="type">UnsafeMutableRawPointer</span></span><br><span class="line"><span class="type">UnsafePointer</span>&lt;<span class="type">Pointee</span>&gt;</span><br><span class="line"><span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Pointee</span>&gt;</span><br><span class="line"><span class="type">Unmanaged</span>&lt;<span class="type">Instance</span>&gt;</span><br></pre></td></tr></table></figure><!--User code is not allowed to extend this list with additional types; this capability is reserved for potential future proposals.--><p>用户不能通过代码手动扩展这个列表；这个权力（能力）会保留给后续的提案。</p><!--To support custom "atomic-representable" types, `AtomicProtocol` also comes with default implementations for all its requirements for `RawRepresentable` types whose `RawValue` is also atomic:--><p>为了支持自定义的 “atomic-representable” 类型，只要一个类型遵循 <code>RawRepresentable</code> 并且它的 <code>RawValue</code> 也是 atomic 的，那 <code>AtomicProtocol</code> 就会提供默认的实现：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">AtomicProtocol</span> <span class="keyword">where</span> <span class="keyword">Self</span>: <span class="type">RawRepresentable</span>, <span class="type">RawValue</span>: <span class="type">AtomicProtocol</span>, <span class="operator">...</span> &#123;</span><br><span class="line">  <span class="comment">// Implementations for all requirements.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The omitted constraint sets up the (private) atomic storage type to match that of the `RawValue`. The default implementations work by converting values to their `rawValue` form, and forwarding all atomic operations to it.--><p>这里省略掉的约束会给 <code>RawValue</code> 类型提供对应的 atomic storage 类型。默认的实现会将它们转成 <code>rawValue</code> 形式，然后转发所有 atomic 操作给它。</p><h4 id="protocol-AtomicInteger"><a href="#protocol-AtomicInteger" class="headerlink" title="protocol AtomicInteger"></a><code>protocol AtomicInteger</code></h4><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">AtomicInteger</span>: <span class="title class_ inherited__">AtomicProtocol</span>, <span class="title class_ inherited__">FixedWidthInteger</span> &#123;</span><br><span class="line">  <span class="comment">// Requirements aren&#x27;t public API.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--(One of the requirements is that atomic integers must serve as their own (private) atomic storage representation.)--><p>（其中一个实现要求是 atomic 整型必须使用它们自身作为 atomic storage）</p><!--Conforming types:--><p>遵循的类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Int</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int64</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int32</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int16</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Int8</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt64</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt32</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt16</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UInt8</span>: <span class="title class_ inherited__">AtomicInteger</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><!--This protocol is not designed to support user-provided conformances.--><p>这个协议的设计不是为了让用户自己提供 conformance。</p><h3 id="Atomic-类型"><a href="#Atomic-类型" class="headerlink" title="Atomic 类型"></a>Atomic 类型</h3><h4 id="struct-UnsafeAtomic"><a href="#struct-UnsafeAtomic" class="headerlink" title="struct UnsafeAtomic&lt;Value&gt;"></a><code>struct UnsafeAtomic&lt;Value&gt;</code></h4><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@frozen</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">UnsafeAtomic</span>&lt;<span class="type">Value</span>: <span class="type">AtomicProtocol</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">@frozen</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Storage</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">init</span>(<span class="keyword">_</span> <span class="params">value</span>: __owned <span class="type">Value</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">@discardableResult</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">dispose</span>() -&gt; <span class="type">Value</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">init</span>(<span class="params">at</span> <span class="params">pointer</span>: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Storage</span>&gt;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">create</span>(<span class="params">initialValue</span>: __owned <span class="type">Value</span>) -&gt; <span class="keyword">Self</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">@discardableResult</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">destroy</span>() -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// Atomic operations:</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">load</span>(<span class="params">ordering</span>: <span class="type">AtomicLoadOrdering</span>) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">store</span>(<span class="keyword">_</span> <span class="params">desired</span>: __owned <span class="type">Value</span>, <span class="params">ordering</span>: <span class="type">AtomicStoreOrdering</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">exchange</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">desired</span>: __owned <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">compareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: __owned <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">compareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: __owned <span class="type">Value</span>,</span><br><span class="line">    <span class="params">successOrdering</span>: <span class="type">AtomicUpdateOrdering</span>,</span><br><span class="line">    <span class="params">failureOrdering</span>: <span class="type">AtomicLoadOrdering</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">weakCompareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: __owned <span class="type">Value</span>,</span><br><span class="line">    <span class="params">successOrdering</span>: <span class="type">AtomicUpdateOrdering</span>,</span><br><span class="line">    <span class="params">failureOrdering</span>: <span class="type">AtomicLoadOrdering</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--`UnsafeAtomic` also provides a handful of integer operations for the standard fixed-width integer types. This is implemented via the `AtomicInteger` protocol.--><p><code>UnsafeAtomic</code> 也给标准库定长的整型类型提供了一些实用的整型操作。这些都是通过 <code>AtomicInteger</code> 协议提供的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> <span class="keyword">where</span> <span class="type">Value</span>: <span class="type">AtomicInteger</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">loadThenWrappingIncrement</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">operand</span>: <span class="type">Value</span> <span class="operator">=</span> <span class="number">1</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">wrappingIncrementThenLoad</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">operand</span>: <span class="type">Value</span> <span class="operator">=</span> <span class="number">1</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">wrappingIncrement</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">operand</span>: <span class="type">Value</span> <span class="operator">=</span> <span class="number">1</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  )</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">loadThenWrappingDecrement</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">operand</span>: <span class="type">Value</span> <span class="operator">=</span> <span class="number">1</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">wrappingDecrementThenLoad</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">operand</span>: <span class="type">Value</span> <span class="operator">=</span> <span class="number">1</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">wrappingDecrement</span>(</span><br><span class="line">    <span class="params">by</span> <span class="params">operand</span>: <span class="type">Value</span> <span class="operator">=</span> <span class="number">1</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  )</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">loadThenBitwiseAnd</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operand</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">bitwiseAndThenLoad</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operand</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">loadThenBitwiseOr</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operand</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">bitwiseOrThenLoad</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operand</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">loadThenBitwiseXor</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operand</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">bitwiseXorThenLoad</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">operand</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">ordering</span>: <span class="type">AtomicUpdateOrdering</span></span><br><span class="line">  ) -&gt; <span class="type">Value</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="struct-UnsafeAtomicLazyReference"><a href="#struct-UnsafeAtomicLazyReference" class="headerlink" title="struct UnsafeAtomicLazyReference&lt;Instance&gt;"></a><code>struct UnsafeAtomicLazyReference&lt;Instance&gt;</code></h4><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">UnsafeAtomicLazyReference</span>&lt;<span class="type">Instance</span>: <span class="type">AnyObject</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">typealias</span> <span class="type">Value</span> <span class="operator">=</span> <span class="type">Instance</span>?</span><br><span class="line"></span><br><span class="line">  <span class="keyword">@frozen</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Storage</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">init</span>()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">@discardableResult</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">dispose</span>() -&gt; <span class="type">Value</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">init</span>(<span class="params">at</span> <span class="params">address</span>: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Storage</span>&gt;)</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">create</span>() -&gt; <span class="keyword">Self</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">@discardableResult</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">destroy</span>() -&gt; <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// Atomic operations:</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">storeIfNil</span>(<span class="keyword">_</span> <span class="params">desired</span>: __owned <span class="type">Instance</span>) -&gt; <span class="type">Instance</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">load</span>() -&gt; <span class="type">Instance</span>?</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a>代码兼容性</h2><!--This is a purely additive change with no source compatibility impact.--><p>这是单纯的附加改动（additive change），不会带来任何代码兼容性的影响。</p><h2 id="对于-ABI-稳定性的影响"><a href="#对于-ABI-稳定性的影响" class="headerlink" title="对于 ABI 稳定性的影响"></a>对于 ABI 稳定性的影响</h2><!--This proposal introduces new entry points to the Standard Library ABI in a standalone `Atomics` module, but otherwise it has no effect on ABI stability.--><p>这个提案会给标准库添加一个独立的 <code>Atomics</code> 模块，但对于 ABI 稳定没有影响。</p><!--On ABI-stable platforms, the struct types and protocols introduced here will become part of the stdlib's ABI with availability matching the first OS releases that include them.--><p>在 ABI 稳定的平台上，结构体和协议会作为标准库 ABI 的一部分，在第一个包含了这些的操作系统上可以使用。</p><!--Most of the atomic methods introduced in this document will be force-inlined directly into client code at every call site. As such, there is no reason to bake them into the stdlib's ABI -- the stdlib binary will not export symbols for them.--><p>这份文档引入的大部分 Atomic 函数都会被强制内联到调用方的代码里。这样，就没有理由将它们包含到标准库的 ABI 里了，因为标准库不会暴露它们的符号。</p><h2 id="对于-API-的影响"><a href="#对于-API-的影响" class="headerlink" title="对于 API 的影响"></a>对于 API 的影响</h2><!--This is an additive change; it has no effect on the API of existing code.--><p>这是单纯的附加改动；它不会对现有代码的 API 产生影响。</p><!--For the new constructs introduced here, the proposed design allows us to make the following changes in future versions of the Swift Standard Library:--><p>这里引入的新结构，提案的设计允许我们在未来对 Swift 标准库做下列改变：</p><!--- Addition of new atomic types (and higher-level constructs built around them). (These new types would not directly back-deploy to OS versions that predate their introduction.)- Addition of new memory orderings. Because all atomic operations compile directly into user code, new memory orderings that we decide to introduce later could potentially back-deploy to any OS release that includes this proposal.- Addition of new atomic operations on the types introduced here. These would be reflected in internal protocol requirements, so they would not be directly back-deployable to previous ABI-stable OS releases.- Introducing a default memory ordering for atomic operations (either by adding a default value to `ordering`, or by adding new overloads that lack that parameter). This too would be a back-deployable change.--><ul><li>添加新的 Atomic 类型（和更高层级的抽象）。（These new types would not directly back-deploy to OS versions that predate their introduction.）</li><li>添加新的 memory orderings。因为所有的 atomic 操作都会直接内联编译到用户代码里，后续决定新引入的 memory ordering 可以部署到任何包含这个提案的 OS 里。</li><li>给这里引入的类型添加新的 atomic 操作，这会影响内部的 protocol requirements，所以他们无法向后部署到 ABI 稳定的系统版本里。</li><li>给 Atomic 操作引入一个默认的 memory ordering（给 <code>ordering</code> 添加一个默认值，或者添加一个新的重载）。这也会是一个可以向后部署的改动。</li></ul><!--(We don't necessarily plan to actually perform any of these changes; we merely leave the door open to doing them.)--><p>（我们不一定会做这些改动，我们只是保留可以这么做的空间）</p><h2 id="未来的方向"><a href="#未来的方向" class="headerlink" title="未来的方向"></a>未来的方向</h2><h3 id="内存安全的-Atomic-结构"><a href="#内存安全的-Atomic-结构" class="headerlink" title="内存安全的 Atomic 结构"></a>内存安全的 Atomic 结构</h3><!--The [Ownership Manifesto] introduces the concept of *non-copiable types* that might enable us to efficiently represent constructs that require a stable (and known) memory location. Atomics and other synchronization tools are classic examples for such constructs, and modeling them with non-copiable types could potentially eliminate the need for unsafe dynamic variables and manual memory management -- a major benefit over the unsafe types in this proposal, with no apparent drawback.--><p><a href="https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md">Ownership Manifesto</a> 引入的<em>non-copiable 类型</em>也许可以让我们更有效地表达那些需要固定内存位置的结构。Atomics 和其它同步工具就是这种结构的典型例子，相比起这个提案里引入的 unsafe 类型，把他们建模为 non-copiable 类型可以减少 unsafe 动态变量和手动内存管理，并且没有显著的缺陷。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">moveonly <span class="keyword">struct</span> <span class="title class_">Atomic</span>&lt;<span class="type">Value</span>: <span class="type">AtomicProtocol</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">typealias</span> <span class="type">Storage</span> <span class="operator">=</span> <span class="type">PrivateAtomicStorage</span>&lt;<span class="type">Value</span>&gt;</span><br><span class="line"></span><br><span class="line">  <span class="meta">@addressable</span> <span class="keyword">private</span> <span class="keyword">var</span> storage: <span class="type">Storage</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">init</span>(<span class="keyword">_</span> <span class="params">value</span>: <span class="type">Value</span>) &#123;</span><br><span class="line">    storage <span class="operator">=</span> <span class="type">Storage</span>(initialValue: value)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">deinit</span> &#123;</span><br><span class="line">    storage.dispose()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">load</span>(<span class="params">ordering</span>: <span class="type">AtomicLoadOrdering</span>) -&gt; <span class="type">Value</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> ptr <span class="operator">=</span> mutablePointer(to: \.storage, in: <span class="keyword">self</span>)</span><br><span class="line">    <span class="keyword">let</span> result <span class="operator">=</span> <span class="type">Storage</span>.atomicLoad(at: ptr, ordering: ordering)</span><br><span class="line">    <span class="keyword">return</span> <span class="type">Storage</span>(decoding: result)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">store</span>(<span class="keyword">_</span> <span class="params">desired</span>: <span class="type">Value</span>, <span class="params">ordering</span>: <span class="type">AtomicStoreOrdering</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> ptr <span class="operator">=</span> mutablePointer(to: \.storage, in: <span class="keyword">self</span>)</span><br><span class="line">    <span class="keyword">let</span> desiredRaw <span class="operator">=</span> <span class="type">Storage</span>(encoding: desired)</span><br><span class="line">    <span class="type">Storage</span>.atomicStore(desiredRaw, at: ptr, ordering: ordering)</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">moveonly <span class="keyword">struct</span> <span class="title class_">UnfairLock</span> &#123;</span><br><span class="line">  <span class="meta">@addressable</span> <span class="keyword">private</span> <span class="keyword">var</span> value: os_unfair_lock</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">init</span>() &#123;</span><br><span class="line">   <span class="keyword">self</span>.value <span class="operator">=</span> os_unfair_lock()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">lock</span>() &#123; </span><br><span class="line">    os_unfair_lock_lock(mutablePointer(to: \.value, in: <span class="keyword">self</span>))</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">unlock</span>() &#123; </span><br><span class="line">    os_unfair_lock_unlock(mutablePointer(to: \.value, in: <span class="keyword">self</span>))</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Note: In addition to non-copiable types, this example also relies on a hypothetical language feature for retrieving the memory location of select stored properties in such types (`@addressable` and `mutablePointer(to:in:)`). As a major simplification, it also assumes that non-copiable types allow mutations to their state within methods not marked `mutating`.--><p>注意：除了不可复制类型之外，这个例子也依赖了一个虚构的语言功能，这个功能可以获取存储属性的内存位置（<code>@addressable</code> 和 <code>mutablePointer(to:in:)</code>）。另外为了简化，它也假设不可复制类型允许在没有标记 <code>mutating</code> 的函数里修改它们的状态。</p><!--Properly designing and implementing these features will require a considerable amount of work. However, we feel it's important to enable work on concurrency features to start even before non-copiable types get implemented. The types introduced in this proposal will not prevent us from introducing memory-safe, non-copiable (or maybe not even movable) atomic types later, if and when it becomes possible to do so.--><p>正确地设计和实现这些功能需要大量的工作。然而，我们觉得让并发功能先启动开发工作更重要，即使不可复制类型还没有实现。当这些功能实现时，提案里引入的这些类型也不会阻碍我们后续引入 memory-safe 和 non-copiable 的 atomic 类型。</p><!--Even though it would be possible today to model safe atomics using class types (e.g., see the `AtomicCounter` example in the discussion above), we believe that the potential additional overhead of a class-based approach wouldn't be acceptable in the long term. Therefore, we prefer to go with an unsafe but low-overhead approach for now, reserving the "nice" `Atomic<Value>` name for future use. (Swift programmers will still be able to define class-based atomics in their own modules if they do not wish to (directly) use unsafe constructs in their synchronization code.)--><p>虽然现在我们可以使用 class 去建模 atomic 类型（例如，上面讨论里的 <code>AtomicCounter</code> 类型），但我们觉得长期而言，基于 class 的方案带来的潜在开销是不可接受的。所以，我们更倾向于 unsafe 但是低开销的方案，把 <code>Atomic&lt;Value&gt;</code> 这个“优雅的”命名保留给后续的提案。（Swift 程序员依旧可以使用基于 class 的 atomics，如果他们不想在同步代码里使用 unsafe 结构）</p><h3 id="双精度-Atomics-和-ABA-问题"><a href="#双精度-Atomics-和-ABA-问题" class="headerlink" title="双精度 Atomics 和 ABA 问题"></a>双精度 Atomics 和 ABA 问题</h3><blockquote><p>译者注：swift-atomics 里已经实现了<a href="https://github.com/apple/swift-atomics/blob/f58501400767a23b8326b6ce005d67b7af6d9010/Sources/Atomics/DoubleWord.swift">双精度的 atomics</a>。</p></blockquote><!--In their current single-word form, atomic pointer and reference types are susceptible to a class of race condition called the *ABA problem*. A freshly allocated object often happens to be placed at the same memory location as a recently deallocated one. Therefore, two successive `load`s of a simple atomic pointer may return the exact same value, even though the pointer may have received an arbitrary number of updates between the two loads, and the pointee may have been completely replaced. This can be a subtle, but deadly source of race conditions in naive implementations of many concurrent data structures.--><p>在它们的目前的单精度形式里，Atomic 指针和引用类型容易遇到一种叫做 ABA 问题的竞态问题。一个被销毁的对象的内存位置，可能会被申请存放另一个新创建的对象。所以，对 Atomic 指针连续两次 <code>load</code> 可能会访问到相同的值，即使指针已经在两次 load 中间已经发生了好几次变更，并且指向的内存已经被完全替换掉了。在原生实现的并发数据结构里，可能会成为一个难以捉摸且致命的竞态问题的来源。</p><!--While the single-word atomic primitives introduced in this document are already useful for some applications, it would be helpful to also provide a set of additional atomic operations that operate on two consecutive `Int`-sized values in the same transaction. All supported architectures provide direct hardware support for such "double-wide" atomic operations.--><p>虽然这份文档里引入的单精度 atomic 原语对于一些应用场景来说已经很实用了，但额外提供一些能够在同一个事务里操作两个 <code>Int</code> 大小的值的 atomic 操作也会很实用。所有支持（atomics）的架构也都提供了双精度 atomic 的硬件支持。</p><!--For example, the second word can be used to augment atomic values with a version counter (sometimes called a "stamp" or a "tag"), which can help resolve the ABA problem by allowing code to reliably verify if a value remained unchanged between two successive loads.--><p>例如，第二个 word 可以用来作为 atomic 值的版本计数器（有时候也被称为 “stamp” 或 “tag”），这样就可以通过检验同一个值在两次 load 之间是否产生过变化来解决 ABA 问题。</p><!--To add support for double-wide atomics within the Standard Library, we need to introduce a representation for their underlying value, including (potentially platform-specific) alignment requirements that match the requirements of the underlying CPU instructions. We consider this to be outside of the scope of this proposal, so we defer double-wide atomics to a separate future proposal.--><p>为了在标准库里支持双精度的 atomic 操作，我们需要引入抽象来描述它们的值，包括（可能 platform-specific）内存对齐的要求，去迎合底层的 CPU 指令。我们认为这些不在这个提案的范围内，所以我们决定推迟到后续的提案里讨论。</p><h3 id="Atomic-强引用和-Memory-Reclamation-问题"><a href="#Atomic-强引用和-Memory-Reclamation-问题" class="headerlink" title="Atomic 强引用和 Memory Reclamation 问题"></a>Atomic 强引用和 Memory Reclamation 问题</h3><blockquote><p>译者注：swift-atomics 里已经实现了 <a href="https://github.com/apple/swift-atomics/blob/f58501400767a23b8326b6ce005d67b7af6d9010/Sources/Atomics/AtomicStrongReference.swift">Atomic 强引用</a>。</p></blockquote><!--Perhaps counter-intuitively, implementing a high-performance, *lock-free* atomic version of regular everyday strong references is not a trivial task. This proposal doesn't attempt to provide such a construct beyond the limited use-case of `UnsafeAtomicLazyReference`.--><p>也许这有点反直觉，但实现一个高性能，无锁的 atomic 强引用并不是一件简单的事情。这个提案并不包含相关的结构去做这件事，除了 <code>UnsafeAtomicLazyReference</code>。</p><!--Under the hood, Swift's strong references have always been using atomic operations to implement reference counting. This allows references to be read (but not mutated) from multiple, concurrent threads of execution, while also ensuring that each object still gets deallocated as soon as its last outstanding reference disappears. However, atomic reference counts on their own do not allow threads to safely share a single *mutable* reference without additional synchronization.--><p>Swift 的强引用总是使用 atomic 操作来实现引用计数。这让引用可以在多线程并发读取（但不包括修改），并且每个对象总能在它最后一个引用消失时销毁。然而，atomic 引用自身需要有额外的同步机制，才能让多个线程共享同一个<em>可变的</em>引用。</p><!--The difficulty is in the implementation of the atomic load operation, which boils down to two separate sub-operations, both of which need to be part of the *same atomic transaction*:--><p>难点在于 atomic load 操作的实现，这里面包含了两个独立的子操作，它们都需要作为<em>同一个 atomic 事务</em>的一部分：</p><!--1. Load the value of the reference.2. Increment the reference count of the corresponding object.--><ol><li>加载引用的值。</li><li>增加对象的引用计数。</li></ol><!--If an intervening store operation were allowed to release the reference between steps 1 and 2, then the loaded reference could already be deallocated by the time `load` tries to increment its refcount.--><p>如果允许一个 store 操作在 1 和 2 中间释放掉引用，那已经读取到的引用就有可能在 <code>load</code> 操作尝试增加引用计数时就已经被回收了。</p><!--Without an efficient way to implement these two steps as a single atomic transaction, the implementation of `store` needs to delay releasing the overwritten value until it can guarantee that every outstanding load operation is completed. Exactly how to implement this is the problem of *memory reclamation* in concurrent data structures.--><p>如果没有一个高效的方式将这两个步骤作为一个 atomic 事务实现的话，那 <code>store</code> 的实现就需要延迟释放被覆盖的值，直到它能保证所有的 load 操作都结束了为止。如何实现它其实就是并发数据结构里的 <em>memory recalmation</em> 问题。</p><!--There are a variety of approaches to tackle this problem, some of which may be general enough to consider in future proposals. (One potential solution can be built on top of double-wide atomic operations, by offloading some of the reference counting operations into the second word of a double-wide atomic reference.)--><p>有很多不同的方式可以绕过这个问题，有些可能足够通用，可以放到后续的提案里。（例如说基于双精度 atomic，在第二个 word 里进行引用计数操作。）</p><!--(It'd be straightforward to use locks to build an atomic strong reference; while such a construct obviously wouldn't be lock-free, it is still a useful abstraction, so it may be a worthy addition to the Standard Library. However, locking constructs are outside the scope of this proposal.)--><p>（atomic 强引用基于锁去实现会更加直观；虽然这样的一个结构显然不是无锁的，但它还是很实用，所以可能也有加入标准库的价值。但这并不在这个提案的范围内。）</p><h3 id="添加新的底层-Atomic-功能"><a href="#添加新的底层-Atomic-功能" class="headerlink" title="添加新的底层 Atomic 功能"></a>添加新的底层 Atomic 功能</h3><!--To enable usecases that require even more fine-grained control over atomic operations, it may be useful to introduce additional low-level atomics features:* support for additional kinds of atomic values (such as double-wide atomics or floating-point atomics [[P0020]]),* new memory orderings, such as a consuming load ordering [[P0750]] or tearable atomics [[P0690]],* "volatile" atomics that prevent certain compiler optimizations* memory fences that only affect the compiler (to prevent single-threaded race conditions such as with signal handlers)* and moreWe defer these for future proposals.--><p>为了支持那些比 atomic 操作粒度更小的操作，额外提供这些底层 atomic 功能可能会有帮助：</p><ul><li>支持更多类型 atomic 值（例如双精度 atomic 和浮点 atomic [<a href="http://wg21.link/P0020">P0020</a>])）</li><li>新的 memory ordering，例如 consuming load ordering [<a href="http://wg21.link/P0750">P0750</a>] 或者 tearable atomics [<a href="http://wg21.link/P0690">P0690</a>]</li><li>避免某些特定编译优化的 “volatile” atomics</li><li>只影响编译器的 memory fences（避免单线程竞态，例如信号处理）</li><li>等等…</li></ul><p>我们会把这些推迟到后续提案里。</p><h2 id="其它替代方案"><a href="#其它替代方案" class="headerlink" title="其它替代方案"></a>其它替代方案</h2><h3 id="默认的-ordering"><a href="#默认的-ordering" class="headerlink" title="默认的 ordering"></a>默认的 ordering</h3><!--We considered defaulting all atomic operations throughout the `Atomics` module to sequentially consistent ordering. While we concede that doing so would make atomics slightly more approachable, implicit ordering values tend to interfere with highly performance-sensitive use cases of atomics (which is *most* use cases of atomics). Sequential consistency tends to be relatively rarely used in these contexts, and implicitly defaulting to it would allow accidental use to easily slip through code review.--><p>我们考虑过把 <code>Atomics</code> 模块里所有的 atomic 操作默认的 order 设置为 sequentially consistent。虽然我们承认这么做可以让 atomics 稍微更加易用，但隐式 ordering 值会影响性能敏感的使用场景（atomics 的<em>主要</em>使用场景）。Sequential consistency 相对来很少使用在这种场景下，并且隐式的默认值更容易让它通过 code review。</p><!--Users who wish for default orderings are welcome to define their own overloads for atomic operations:--><p>希望使用默认 ordering 的用户可以定义函数去重载：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">load</span>() -&gt; <span class="type">Value</span> &#123; </span><br><span class="line">    load(ordering: .sequentiallyConsistent)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">store</span>(<span class="keyword">_</span> <span class="params">desired</span>: <span class="type">Value</span>) &#123; </span><br><span class="line">    store(desired, ordering: .sequentiallyConsistent) </span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">exchange</span>(<span class="keyword">_</span> <span class="params">desired</span>: <span class="type">Value</span>) -&gt; <span class="type">Value</span> &#123;</span><br><span class="line">    exchange(desired, ordering: .sequentiallyConsistent)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">compareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: <span class="type">Value</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>) &#123;</span><br><span class="line">    compareExchange(</span><br><span class="line">      expected: expected, </span><br><span class="line">      desired: desired, </span><br><span class="line">      ordering: .sequentiallyConsistent)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">weakCompareExchange</span>(</span><br><span class="line">    <span class="params">expected</span>: <span class="type">Value</span>,</span><br><span class="line">    <span class="params">desired</span>: <span class="type">Value</span></span><br><span class="line">  ) -&gt; (exchanged: <span class="type">Bool</span>, original: <span class="type">Value</span>) &#123;</span><br><span class="line">    weakCompareExchange(</span><br><span class="line">      expected: expected, </span><br><span class="line">      desired: desired, </span><br><span class="line">      successOrdering: .sequentiallyConsistent,</span><br><span class="line">      failureOrdering: .sequentiallyConsistent)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">UnsafeAtomic</span> <span class="keyword">where</span> <span class="type">Value</span>: <span class="type">AtomicInteger</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">wrappingIncrement</span>(<span class="params">by</span> <span class="params">delta</span>: <span class="type">Value</span> <span class="operator">=</span> <span class="number">1</span>) &#123;</span><br><span class="line">    wrappingIncrement(by: delta, ordering: .sequentiallyConsistent)</span><br><span class="line">  &#125;</span><br><span class="line">  etc.</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="UnsafeAtomic-类型改用别的命名"><a href="#UnsafeAtomic-类型改用别的命名" class="headerlink" title="UnsafeAtomic 类型改用别的命名"></a><code>UnsafeAtomic</code> 类型改用别的命名</h3><!--We briefly considered naming the unsafe atomic reference types in this proposal `UnsafePointerToAtomic` and `UnsafePointerToAtomicLazyReference`, to highlight the fact that they are simple wrappers around unsafe pointer types.--><p>我们曾经考虑过将提案里的 unsafe atomic 引用类型命名为 <code>UnsafePointerToAtomic</code> 和 <code>UnsafePointerToAtomicLazyReference</code>，强调它们只是 unsafe pointer 的简单封装。</p><!--However, after living on these names for a while, we had to reject them as unsuitable. These new generic types *emphatically aren't* pointers -- they merely happen to contain a pointer value in their internal representation. --><p>然而，实际使用了这些命名一段时间后，由于不合适所以我们不得不拒绝这个命名。这些新的泛型类型<em>显然不是指针</em> —— 它们只是在内部实现里使用了指针。</p><!--It's far more instructive to think of these types as unsafe precursors to corresponding non-copiable constructs, allowing us to fully define and start using the functionality they will eventually provide even before non-copiable types become available in the language.--><p>把这些类型想象成对应的 non-copiable 结构的 unsafe 先行版本会更恰当，让我们可以在语言支持 non-copiable 类型之前就定义并且开始使用这些功能。</p><!--We expect code using these unsafe precursors will be easily upgradeable to their eventual non-copiable variants when it becomes possible to implement those. In the meantime, since these are memory-unsafe variants of eventual `Atomic` and `AtomicLazyReference` types, it seems appropriate to simply prefix their names with the customary `Unsafe` prefix.--><p>我们期望这些使用 unsafe 先行版本的代码，在未来可以轻松地升级到 non-copiable 实现的版本。同时，因为它们是 <code>Atomic</code> 和 <code>AtomicLazyReference</code> 类型的 unsafe 版，在他们的命名前面直接加上 <code>Unsafe</code> 前缀也非常合理。</p><!--Logically, `UnsafeAtomic` and `UnsafeAtomicLazyReference` are both reference types with an independent storage representation and manual memory management. The common set of APIs between these types establishes a new pattern geared specifically for modeling low-overhead synchronization constructs in current versions of Swift:--><p>逻辑上来说，<code>UnsafeAtomic</code> 和 <code>UnsafeAtomicLazyReference</code> 都是引用类型，使用自治的存储形式，需要手动管理内存。它们共享的这套 API 建立起一个新的模式，可以在现有版本的 Swift 驱动低开销的同步结构：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UnsafeDemo</span> &#123;</span><br><span class="line">  <span class="keyword">typealias</span> <span class="type">Value</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">struct</span> <span class="title class_">Storage</span> &#123;</span><br><span class="line">    <span class="comment">// Initialize a new storage instance by converting the given value.</span></span><br><span class="line">    <span class="comment">// The conversion may involve side effects such as unbalanced retain/release</span></span><br><span class="line">    <span class="comment">// operations; to ensure correct results, the resulting storage instance</span></span><br><span class="line">    <span class="comment">// must be used to initialize exactly one memory location.</span></span><br><span class="line">    <span class="keyword">init</span>(<span class="keyword">_</span> <span class="params">initialValue</span>: __owned <span class="type">Value</span>)</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Dispose of this storage instance, and return the last stored value.</span></span><br><span class="line">    <span class="comment">// This undoes any side effects that happened when the value was stored.</span></span><br><span class="line">    <span class="comment">// (For example, it may balance previous retain/release operations.)</span></span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="comment">// Note: this is different from deinitializing a memory location holding</span></span><br><span class="line">    <span class="comment">// a Storage value.</span></span><br><span class="line">    <span class="keyword">@discardableResult</span></span><br><span class="line">    <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">dispose</span>() -&gt; <span class="type">Value</span></span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Initialize a new instance using the specified storage location.</span></span><br><span class="line">  <span class="comment">// The caller code must have previously initialized the storage location.</span></span><br><span class="line">  <span class="comment">//</span></span><br><span class="line">  <span class="comment">// It is the caller&#x27;s code responsibility to keep the storage location</span></span><br><span class="line">  <span class="comment">// valid while accessing it through the resulting instance,</span></span><br><span class="line">  <span class="comment">// and to correctly dispose of the storage value at the end of its useful life.</span></span><br><span class="line">  <span class="keyword">init</span>(<span class="params">at</span> <span class="params">address</span>: <span class="type">UnsafeMutablePointer</span>&lt;<span class="type">Storage</span>&gt;)</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Return a new instance by allocating and initializing a dynamic variable</span></span><br><span class="line">  <span class="comment">// dedicated to holding its storage. Must be paired with a call to `destroy()`.</span></span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">create</span>(<span class="params">initialValue</span>: <span class="type">Value</span>) -&gt; <span class="keyword">Self</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// Destroy an instance previously created by `Self.create(initialValue:)`,</span></span><br><span class="line">  <span class="comment">// deinitializing and deallocating the dynamic variable that backs it,</span></span><br><span class="line">  <span class="comment">// and returning the last value it held before destruction.</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">destroy</span>() -&gt; <span class="type">Value</span></span><br><span class="line">  </span><br><span class="line">  <span class="operator">...</span> <span class="comment">// Custom operations</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--Future proposals may add additional low-level synchronization constructs conforming to the same pattern. The advent of non-copiable types will eventually (mostly) obsolete the need for this pattern; although we may decide to keep these unsafe precursors around if their flexibility proves useful.--><p>未来的提案也许会遵循着同样的模式添加新的底层同步结构。non-copiable 类型的出现最终会让我们不再需要这种模式；不过如果我们需要这种灵活性的话，应该也会继续保留这些 unsafe 结构。</p><h3 id="一个真正泛用的-Atomic-泛型类型"><a href="#一个真正泛用的-Atomic-泛型类型" class="headerlink" title="一个真正泛用的 Atomic 泛型类型"></a>一个真正泛用的 Atomic 泛型类型</h3><!--While future proposals may add a variety of other atomic types, we do not expect to ever provide a truly universal generic `Atomic<T>` construct. The Atomics module is designed to provide high-performance wait-free primitives, and these are heavily constrained by the atomic instruction sets of the CPU architectures Swift targets.--><p>未来的提案也许会提供其它的 atomic 类型，但我们不认为以后会提供一个真正的泛型 <code>Atomic&lt;T&gt;</code> 结构。Atomics 模块的设计是为了提供高性能，无等待的原语，这些会被 Swift 支持的 CPU 架构提供的 atomic 指令约束。</p><!--A universal `Atomic<T>` type that can hold *any* value is unlikely to be implementable without locks, so it is outside the scope of this proposal -- and indeed, it is outside the scope of the Atomics module in general. We may eventually consider adding such a construct in a future concurrency proposal:--><p>一个普适的，可以持有<em>任何</em>值的 <code>Atomic&lt;T&gt;</code> 类型不可能基于无锁实现，所以它不在这个提案的范围内 —— 并且，它也不在 Atomics 模块的范围内。最终我们可能会考虑在未来的并发提案里加入这样一个结构：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">@propertyWrapper</span></span><br><span class="line">moveonly <span class="keyword">struct</span> <span class="title class_">Serialized</span>&lt;<span class="type">Value</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">let</span> _lock <span class="operator">=</span> <span class="type">UnfairLock</span>()</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">var</span> _value: <span class="type">Value</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">init</span>(<span class="params">wrappedValue</span>: <span class="type">Value</span>) &#123;</span><br><span class="line">    <span class="keyword">self</span>._value <span class="operator">=</span> wrappedValue</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> wrappedValue: <span class="type">Value</span> &#123;</span><br><span class="line">    <span class="keyword">get</span> &#123; _lock.locked &#123; _value &#125; &#125;</span><br><span class="line">    modify &#123; </span><br><span class="line">      _lock.lock()</span><br><span class="line">      <span class="keyword">defer</span> &#123; _lock.unlock() &#125;</span><br><span class="line">      yield <span class="operator">&amp;</span>_value</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="提供一个-value-属性"><a href="#提供一个-value-属性" class="headerlink" title="提供一个 value 属性"></a>提供一个 <code>value</code> 属性</h3><!--Our atomic constructs are unusual because even though semantically they behave like containers holding a value, they do not provide direct access to it. Instead of exposing a getter and a setter on a handy `value` property, they expose cumbersome `load` and `store` methods. There are two reasons for this curious inconsistency:--><p>我们的 atomic 结构并不常见，因为它们语义上像是持有某个值的容器，但实际上他们并不提供直接的访问。它们提供了笨拙的 <code>load</code> 和 <code>store</code> 函数，而不是给 <code>value</code> 属性提供 getter 和 setter。这个不一致有两个理由：</p><!--First, there is the obvious issue that property getter/setters have no room for an ordering parameter.--><p>第一，直接提供一个带有 getter&#x2F;setter 的属性最明显的问题就是无法指定 ordering 参数。</p><!--Second, there is a deep underlying problem with the property syntax: it encourages silent race conditions. For example, consider the code below:--><p>第二，property 语法有一个潜在的问题：它鼓励用户忽略竞态。例如下面的代码：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> counter <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line"><span class="operator">...</span></span><br><span class="line">counter.value <span class="operator">+=</span> <span class="number">1</span></span><br></pre></td></tr></table></figure><!--Even though this increment looks like it may be a single atomic operation, it gets executed as two separate atomic transactions:--><p>虽然这个自增操作看起来像是一个单独的 atomic 操作，但实际上它的执行会触发两个 atomic 事务：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> temp <span class="operator">=</span> counter.value <span class="comment">// atomic load</span></span><br><span class="line">temp <span class="operator">+=</span> <span class="number">1</span></span><br><span class="line">counter.value <span class="operator">=</span> temp     <span class="comment">// atomic store</span></span><br></pre></td></tr></table></figure><!--If some other thread happens to update the value after the atomic load, then that update gets overwritten by the subsequent store, resulting in data loss.--><p>如果其它线程刚好在 atomic load 之后更新了这个值，那么这个更新就会被后续的 store 给覆盖，导致数据丢失。</p><!--To prevent this gotcha, none of the proposed atomic types provide a property for accessing their value, and we don't foresee adding such a property in the future, either.--><p>为了避免这个问题，提案里的 atomic 类型都不会提供这么一个属性去访问它们的值，并且可见的未来我们也不会考虑添加这么一个属性。</p><!--(Note that this problem cannot be mitigated by implementing [modify accessors]. Lock-free updates cannot be implemented without the ability to retry the update multiple times, and modify accessors can only yield once.)--><p>（注意，这个问题也没办法通过 <a href="https://forums.swift.org/t/modify-accessors/31872">modify accessors</a> 的实现去解决。无锁更新不可能在不重试的情况实现，并且 modify 访问也只能 yield 一次）</p><h3 id="Memory-Orderings-的另一套设计方案"><a href="#Memory-Orderings-的另一套设计方案" class="headerlink" title="Memory Orderings 的另一套设计方案"></a>Memory Orderings 的另一套设计方案</h3><!--Modeling memory orderings with enumeration(-like) values fits well into the Standard Library's existing API design practice, but `ordering` arguments aren't without problems. Most importantly, the quality of code generation depends greatly on the compiler's ability to constant-fold switch statements over these ordering values into a single instruction. This can be fragile -- especially in unoptimized builds. We think [constraining these arguments to compile-time constants](#restricting-ordering-arguments-to-compile-time-constants) strikes a good balance between readability and performance, but it's instructive to look at some of the approaches we considered before settling on this choice.--><p>将 memory orderings 建模为类似于枚举的值，非常符合标准库现有的 API 设计，但 <code>ordering</code> 参数不是完美的。最重要的是，代码生成的质量取决于编译器的能力，看它是否能通过 constant-fold 将 ordering 值的 switch 语句压缩成一条指令。这会让逻辑变得脆弱 —— 特别是在无优化的 build 里。我们认为<a href="#%E5%B0%86-Ordering-%E5%8F%82%E6%95%B0%E7%BA%A6%E6%9D%9F%E4%B8%BA%E7%BC%96%E8%AF%91%E6%9C%9F%E5%8F%AF%E6%8E%A8%E5%AF%BC%E7%9A%84%E5%B8%B8%E9%87%8F">将这些参数约束为编译器可推导的常量</a>在可读性和性能中间取得一个很好的平衡，但在一切尘埃落定之前也可以看看还有没有更好的选择。</p><h4 id="将-ordering-编码到函数名里"><a href="#将-ordering-编码到函数名里" class="headerlink" title="将 ordering 编码到函数名里"></a>将 ordering 编码到函数名里</h4><!--One obvious idea is to put the ordering values directly in the method name for every atomic operation. This would be easy to implement but it leads to practically unusable API names. Consider the two-ordering compare/exchange variant below:--><p>一个显而易见的方法就是把 ordering 直接放到函数名里。这样实现起来就会很简单，但它也导致 API 变得不好用，例如下面 compare &#x2F; exchange 的变种：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">flag.sequentiallyConsistentButAcquiringAndReleasingOnFailureCompareExchange(</span><br><span class="line">  expected: <span class="number">0</span>,</span><br><span class="line">  desired: <span class="number">1</span>)</span><br></pre></td></tr></table></figure><!--We could find shorter names for the orderings (`Serialized`, `Barrier` etc.), but ultimately the problem is that this approach tries to cram too much information into the method name, and the resulting multitude of similar-but-not-exactly-the-same methods become an ill-structured mess.--><p>我们可以使用更短的名字（例如 <code>Serialized</code>，<code>Barrier</code> 等），但核心问题是这种方式将太多信息塞到函数名里，很多相似但又不相同的函数最终会变成一团乱麻。</p><h4 id="将-ordering-建模为泛型参数"><a href="#将-ordering-建模为泛型参数" class="headerlink" title="将 ordering 建模为泛型参数"></a>将 ordering 建模为泛型参数</h4><!--A second idea is model the orderings as generic type parameters on the atomic types themselves.--><p>第二个方案是把 ordering 建模成 atomic 类型的泛型参数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UnsafeAtomic</span>&lt;<span class="type">Value</span>: <span class="type">AtomicProtocol</span>, <span class="type">Ordering</span>: <span class="type">AtomicMemoryOrdering</span>&gt; &#123;</span><br><span class="line">  <span class="operator">...</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> counter <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>, <span class="type">Relaxed</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line">counter.wrappingIncrement()</span><br></pre></td></tr></table></figure><!--This simplifies the typical case where all operations on a certain atomic value use the same "level" of ordering (relaxed, acquire/release, or sequentially consistent). However, there are considerable drawbacks:--><p>这样可以简化只使用同一个 “level” 的 ordering 的 atomic 值。然而，这就有很多缺点：</p><!--* This design puts the ordering specification far away from the actual operations -- obfuscating their meaning. * It makes it a lot more difficult to use custom orderings for specific operations (like the speculative relaxed load in the `wrappingIncrement` example in the section on [Atomic Operations](#atomic-operations) above).* We wouldn't be able to provide a default value for a generic type parameter. * Finally, there is also the risk of unspecialized generics interfering with runtime performance.--><ul><li>这个设计把 ordering 从 atomic 操作中剥离出来 —— 混淆了它的含义。</li><li>特定的操作想要指定 ordering 变得很困难（就像前面 <a href="#%E5%9F%BA%E7%A1%80%E7%9A%84-Atomic-%E6%93%8D%E4%BD%9C">Atomic 操作</a> 小节里提到的 <code>wrappingIncrement</code> 的例子）。</li><li>我们没办法给泛型参数提供默认值。</li><li>最后，没有特化过的泛型可能会影响运行时性能。</li></ul><h4 id="Ordering-Views"><a href="#Ordering-Views" class="headerlink" title="Ordering Views"></a>Ordering Views</h4><!--The most promising alternative idea to represent memory orderings was to model them like `String`'s encoding views:--><p>最有潜力的替代方案是将 memory ordering 建模成类似于 <code>String</code> 的 encoding view 那样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> counter <span class="operator">=</span> <span class="type">UnsafeAtomic</span>&lt;<span class="type">Int</span>&gt;.create(initialValue: <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">counter.relaxed.increment()</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> current <span class="operator">=</span> counter.acquiring.load()</span><br></pre></td></tr></table></figure><!--There are some things that we really like about this "ordering view" approach:- It eliminates the need to ever switch over orderings, preventing any and all constant folding issues.- It makes it obvious that memory orderings are supposed to be compile-time parameters.- The syntax is arguably more attractive.--><p>这个 “ordering view” 的方案有几个点我们很喜欢；</p><ul><li>它不需要去 switch ordering，避免了所有 constant folding 相关的问题。</li><li>它让 memory ordering 看起来就应该成为编译时的参数。</li><li>语法优雅。</li></ul><!--However, we ultimately decided against going down this route, for the following reasons:--><p>然而，我们最终还是决定不走这条路，因为这些原因：</p><!--  - **Composability.** Such ordering views are unwieldy for the variant of `compareExchange` that takes separate success/failure orderings. Ordering views don't nest very well at all:  - **API surface area and complexity.** Ordering views act like a multiplier for API entry points. In our prototype implementation, introducing ordering views increased the API surface area of atomics by 3×: we went from 6 public structs with 53 public methods to 27 structs with 175 methods. While clever use of protocols and generics could reduce this factor, the increased complexity seems undesirable. (E.g., generic ordering views would reintroduce potential performance problems in the form of unspecialized generics.)    API surface area is not necessarily the most important statistic, but public methods do have some cost. (In e.g. the size of the stdlib module, API documentation etc.)  - **Unintuitive syntax.** While the syntax is indeed superficially attractive, it feels backward to put the memory ordering *before* the actual operation. While memory orderings are important, I suspect most people would consider them secondary to the operations themselves.  - **Limited Reuse.** Implementing ordering views takes a rather large amount of (error-prone) boilerplate-heavy code that is not directly reusable. Every new atomic type would need to implement a new set of ordering views, tailor-fit to its own use-case.--><ul><li>**可组合性。**ordering view 对于 <code>compareExchange</code> 这种需要分别指定成功&#x2F;失败的 ordering 的情况太过于笨重。ordering view 没办法很好的嵌套：<figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">counter.acquiringAndReleasing.butAcquiringOnFailure.compareExchange(<span class="operator">...</span>)</span><br></pre></td></tr></table></figure></li><li><strong>API 会变得很多很杂</strong>。Ordering views 会让 API 指数级增长。在我们的原型实现里，引入 ordering views 让 atomics 的 API 增加了 3 倍；我们从 6 个结构体 53 个函数变成 27 个结构体 175 个函数。虽然使用协议和泛型可以缓解这个情况，但复杂度会增加。（例如，泛型 ordering views 又会因为未特化的泛型导致性能问题）<br>API 增多不是最重要的考量，但公开函数确实会产生一些损耗（例如，标准库的体积，API 文档等等）   </li><li><strong>不直观的语法</strong>。虽然语法层面上看起来确实很诱人，但把 memory ordering 放到操作前面似乎有些本末倒置。虽然 memory ordering 很重要，但我认为大多数人都会把它们排在操作后面。</li><li><strong>有限的复用</strong>。实现 ordering views 需要大量的模版代码，而不是直接复用。每一个新的 Atomic 类型都需要实现所有这些 ordering views，并且根据它的使用场景进行增删。</li></ul><h2 id="引用资料"><a href="#引用资料" class="headerlink" title="引用资料"></a>引用资料</h2><ul><li><strong>[<a href="https://github.com/apple/swift/blob/master/docs/OwnershipManifesto.md">Ownership Manifesto</a>]</strong> John McCall. “Ownership Manifesto.” <em>Swift compiler documentation</em>, May 2, 2017.</li><li><strong>[<a href="https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md">SE-0176</a>]</strong> John McCall. “Enforce Exclusive Access to Memory. <em>Swift Evolution Proposal,</em> SE-0176, May 2, 2017. </li><li><strong>[<a href="https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md">Generics Manifesto</a>]</strong> Douglas Gregor. “Generics Manifesto.” <em>Swift compiler documentation</em>, 2016. </li><li><strong>[<a href="https://isocpp.org/std/the-standard">C++17</a>]</strong> ISO&#x2F;IEC. <em>ISO International Standard ISO&#x2F;IEC 14882:2017(E) – Programming Language C++.</em> 2017.</li><li><strong>[Williams 2019]</strong> Anthony Williams. <em>C++ Concurrency in Action.</em> 2nd ed., Manning, 2019.</li><li><strong>[Nagarajan 2020]</strong> Vijay Nagarajan, Daniel J. Sorin, Mark D. Hill, David A. Wood. <em>A Primer on Memory Consistency and Cache Coherence.</em> 2nd ed., Morgan &amp; Claypool, February 2020. </li><li><strong>[Herlihy 2012]</strong> Maurice Herlihy, Nir Shavit. <em>The Art of Multiprocessor Programming.</em> Revised 1st ed., Morgan Kauffmann, May 2012.</li><li><strong>[<a href="https://doi.org/10.1145/1375581.1375591">Boehm 2008</a>]</strong> Hans-J. Boehm, Sarita V. Adve. “Foundations of the C++ Concurrency Memory Model.” In <em>PLDI ‘08: Proc. of the 29th ACM SIGPLAN Conf. on Programming Language Design and Implementation</em>, pages 68–78, June 2008. </li><li><strong>[<a href="https://doi.org/10.1145/1925844.1926394">Batty 2011</a>]</strong> Mark Batty, Scott Owens, Susmit Sarkar, Peter Sewell, Tjark Weber. “Mathematizing C++ Concurrency.” In <em>ACM SIGPlan Not.,</em> volume 46, issue 1, pages 55–66, January 2011. </li><li><strong>[<a href="https://doi.org/10.1145/2247684.2247688">Boehm 2012</a>]</strong> Hans-J. Boehm. “Can Seqlocks Get Along With Programming Language Memory Models?” In <em>MSPC ‘12: Proc. of the 2012 ACM SIGPLAN Workshop on Memory Systems Performance and Correctness</em>, pages 12–20, June 2012. </li><li><strong>[<a href="https://doi.org/10.1145/2983990.2983997">Nienhuis 2016</a>]</strong> Kyndylan Nienhuis, Kayvan Memarian, Peter Sewell. “An Operational Semantics for C&#x2F;C++11 Concurrency.” In <em>OOPSLA 2016: Proc. of the 2016 ACM SIGPLAN Conf. on Object Oriented Programming, Systems, Languages, and Applications,</em> pages 111–128, October 2016. </li><li><strong>[<a href="https://doi.org/10.1007/978-3-319-89963-3_4">Mattarei 2018</a>]</strong> Christian Mattarei, Clark Barrett, Shu-yu Guo, Bradley Nelson, Ben Smith. “EMME: a formal tool for ECMAScript Memory Model Evaluation.” In <em>TACAS 2018: Lecture Notes in Computer Science</em>, vol 10806, pages 55–71, Springer, 2018. </li><li><strong>[<a href="http://wg21.link/N2153">N2153</a>]</strong> Raúl Silvera, Michael Wong, Paul McKenney, Bob Blainey. <em>A simple and efficient memory model for weakly-ordered architectures.</em> WG21&#x2F;N2153, January 12, 2007. </li><li><strong>[<a href="http://wg21.link/N4455">N4455</a>]</strong> JF Bastien <em>No Sane Compiler Would Optimize Atomics.</em> WG21&#x2F;N4455, April 10, 2015. </li><li><strong>[<a href="http://wg21.link/P0020">P0020</a>]</strong> H. Carter Edwards, Hans Boehm, Olivier Giroux, JF Bastien, James Reus. <em>Floating Point Atomic.</em> WG21&#x2F;P0020r6, November 10, 2017. </li><li><strong>[<a href="http://wg21.link/P0124">P0124</a>]</strong> Paul E. McKenney, Ulrich Weigand, Andrea Parri, Boqun Feng. <em>Linux-Kernel Memory Model.</em> WG21&#x2F;P0124r6. September 27, 2018. </li><li><strong>[<a href="http://wg21.link/P0418">P0418</a>]</strong> JF Bastien, Hans-J. Boehm. <em>Fail or succeed: there is no atomic lattice.</em> WG21&#x2F;P0417r2, November 9, 2016. </li><li><strong>[<a href="http://wg21.link/P0690">P0690</a>]</strong> JF Bastien, Billy Robert O’Neal III, Andrew Hunter. <em>Tearable Atomics.</em> WG21&#x2F;P0690, February 10, 2018. </li><li><strong>[<a href="http://wg21.link/P0735">P0735</a>]</strong>: Will Deacon, Jade Alglave. <em>Interaction of <code>memory_order_consume</code> with release sequences.</em> WG21&#x2F;P0735r1, June 17, 2019. </li><li><strong>[<a href="http://wg21.link/P0750">P0750</a>]</strong> JF Bastien, Paul E. McKinney. <em>Consume</em>. WG21&#x2F;P0750, February 11, 2018. </li><li><strong>[<a href="https://developer.apple.com/documentation/code_diagnostics/thread_sanitizer">TSan1</a>]</strong> <em>Thread Sanitizer – Audit threading issues in your code.</em> Apple Developer Documentation. Retrieved March 2020. </li><li><strong>[<a href="https://clang.llvm.org/docs/ThreadSanitizer.html">TSan2</a>]</strong> <em>ThreadSanitizer</em>. Clang 11 documentation. Retrieved March 2020.</li></ul><!-- Local Variables: --><!-- mode: markdown --><!-- fill-column: 10000 --><!-- eval: (setq-local whitespace-style '(face tabs newline empty)) --><!-- eval: (whitespace-mode 1) --><!-- eval: (visual-line-mode 1) --><!-- End: -->]]>
    </content>
    <id>https://kemchenj.github.io/2020-10-02/</id>
    <link href="https://kemchenj.github.io/2020-10-02/"/>
    <published>2020-10-02T16:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>原文链接：<a href="https://github.com/apple/swift-evolution/blob/3a358a07e878a58bec256639d2beb48461fc3177/proposals/0282-atomics.md">SE-0282 Low-Level Atomic Operations</a></p>
<p>译者注：</p>
<p>前两天 Apple 的团队发布了 <a href="https://github.com/apple/swift-atomics">swift-atomics</a>，让我们可以用纯 Swift 代码实现高性能的无锁并发数据结构，这里我就把它的前身 <a href="https://github.com/apple/swift-evolution/blob/3a358a07e878a58bec256639d2beb48461fc3177/proposals/0282-atomics.md">SE-0282 Low-Level Atomic Operations</a> 翻译出来，希望可以帮助大家更好地理解 swift-atomics，也欢迎留言指正。</p>
<p>本文翻译的这个版本与最终版本的提案有比较大的出入，提案<a href="https://forums.swift.org/t/se-0282-low-level-atomic-operations/35382/60">在 Joe 的提议下</a>，把 Atomics 作为一个独立的 Swift Package 发布出来，摆脱 API&#x2F;ABI 稳定的顾虑，也不需要跟着 Swift 的发布周期走，可以快速地进行迭代。</p>
<p>并且这个版本的提案内容最终被拆分成了三个部分进行实现：</p>
<ul>
<li>对于编译器必要的修改作为私有功能直接合并到主分支里。</li>
<li>对于内存模型的定义演变成 <a href="https://github.com/apple/swift-evolution/blob/main/proposals/0282-atomics.md">SE-0282 Clarify the Swift memory consistency model ⚛︎</a> 提案，并且优化了与 C Atomics 的交互，最终提案在 Swift 5.3 里完成了实现。</li>
<li>核心的功能改为在 C Atomics 的基础上实现，作为 <a href="https://github.com/apple/swift-atomics">swift-atomics</a> 发布出来。</li>
</ul>
</blockquote>]]>
    </summary>
    <title>【译】SE-0282 Atomic 底层操作 ⚛︎</title>
    <updated>2026-09-02T11:08:19.193Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>虽然 Swift Package Manager 发布到现在已经四年了，但采用率一直很低，无法完全替代 Cocoapods，不过今年情况将会有所改善，SwiftPM 今年迎来两个非常重要的功能：</p><ol><li>二进制依赖分发</li><li>资源文件</li></ol><span id="more"></span><h2 id="二进制依赖"><a href="#二进制依赖" class="headerlink" title="二进制依赖"></a>二进制依赖</h2><p>在去年 Xcode 11 集成了 SwiftPM 的功能，让我们可以以源码形式分发 Library，并且也引入了 XCFramework 来分发闭源 Library。</p><p>今年 Xcode 12 将会把它们结合到一起，提供二进制依赖库的支持，在下面的章节里我们将会介绍如何集成，分发和制作二进制依赖。</p><h3 id="集成"><a href="#集成" class="headerlink" title="集成"></a>集成</h3><p>包含了二进制文件的 Swift Package 在集成时不需要任何特殊的设置，它们也是一个普通的 <code>Package.product</code>，像之前一样在 target 的 <code>dependencies</code> 里通过名字指定即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">package</span> <span class="operator">=</span> <span class="type">Package</span>(</span><br><span class="line">    name: <span class="string">&quot;package&quot;</span>,</span><br><span class="line">    products: [</span><br><span class="line">        .executable(name: <span class="string">&quot;package&quot;</span>, targets: [<span class="string">&quot;package&quot;</span>])</span><br><span class="line">    ],</span><br><span class="line">    dependencies: [</span><br><span class="line">        .package(<span class="string">&quot;https://github.com/JohnnyAppleased2020/BinaryEmoji&quot;</span>, from: <span class="string">&quot;1.0.0&quot;</span>)</span><br><span class="line">    ],</span><br><span class="line">    targets: [</span><br><span class="line">        .target(name: <span class="string">&quot;package&quot;</span>, dependencies: [<span class="string">&quot;Emoji&quot;</span>])</span><br><span class="line">    ]</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="分发"><a href="#分发" class="headerlink" title="分发"></a>分发</h3><p>需要注意，二进制依赖在 Swift 5.3 及以上才能使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// swift-tools-version: 5.3</span></span><br></pre></td></tr></table></figure><p>Swift 5.3 里新增了一种新的 <code>Target</code> 类型 <code>binaryTarget</code> 来指定打包好的二进制文件：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">package</span> <span class="operator">=</span> <span class="type">Package</span>(</span><br><span class="line">    name: <span class="string">&quot;Emoji&quot;</span>,</span><br><span class="line">    products: [</span><br><span class="line">        .library(name: <span class="string">&quot;Emoji&quot;</span>, targets: [<span class="string">&quot;Emoji&quot;</span>]),</span><br><span class="line">    ],</span><br><span class="line">    targets: [</span><br><span class="line">        .binaryTarget(</span><br><span class="line">            name: <span class="string">&quot;Emoji&quot;</span>,</span><br><span class="line">            url: <span class="string">&quot;https://example.com/emoji/Emoji-1.0.0.xcframework.zip&quot;</span>,</span><br><span class="line">            checksum: <span class="string">&quot;6d9888a1a27418674b4d7c31732f6d60e60734ceb11a0ce9b54d1871918d9c194&quot;</span></span><br><span class="line">        )</span><br><span class="line">    ]</span><br><span class="line">)    </span><br></pre></td></tr></table></figure><p>在分发二进制依赖时需要注意几点：</p><ul><li>目前只支持苹果平台，为了实现的便捷复用了已有的 XCFramework 格式，它支持动态和静态链接，并且可以同时支持多个平台。</li><li>支持本地路径或者 https 链接。</li><li>在使用本地路径时指向的可以是 XCFramework 的路径或者是 XCFramework 压缩后的 zip 文件，而 https 链接则只能指向 zip 文件。</li></ul><h3 id="制作"><a href="#制作" class="headerlink" title="制作"></a>制作</h3><p>目前 XCFramework 的制作必须依托于 <code>xcodebuild</code>，所以需要先使用 <code>swift package generate-xcodeproj</code> 生成 Xcode 项目文件，然后：</p><ol><li>在 Build Settings 里将 <code>Build Libraries for Distribution</code> 选项改为 <code>YES</code>。</li><li>使用 <code>xcodebuild archive</code> 打包 framework 文件。</li><li>使用 <code>xcodebuild -create-xcframework</code> 将各个平台的 framework 文件合并为 xcframework。</li></ol><blockquote><p>更具体的细节可以查看 <a href="https://developer.apple.com/videos/play/wwdc2019/416/">Binary Frameworks in Swift - WWDC2019</a>。</p></blockquote><h2 id="资源文件"><a href="#资源文件" class="headerlink" title="资源文件"></a>资源文件</h2><p>今年的 Xcode 12 我们可以给 Swift Package 添加图片，storyboard 以及其它资源文件，同时也可以本地化这些资源文件。并且资源文件的添加使用的是现有的 API，所以也兼容之前版本的操作系统。</p><h3 id="添加资源文件"><a href="#添加资源文件" class="headerlink" title="添加资源文件"></a>添加资源文件</h3><p>SwiftPM 会根据文件的拓展名来进行处理，有一部分文件的使用目的非常明确，Xcode 会自动帮我们处理，只要添加到目录里即可：</p><p><img src="/images/Screen%20Shot%202020-06-28%20at%2021.39.49.webp" alt="Screen Shot 2020-06-28 at 21.39.49"></p><p>但有一部分文件它们的使用目的并不明确，例如 shell 脚本，文件夹，它们可能并不需要打包到 Package 里，此时我们就需要手动声明这些文件的处理规则：</p><p><img src="/images/Screen%20Shot%202020-06-28%20at%2021.39.30%201.webp" alt="Screen Shot 2020-06-28 at 21.39.30 1"></p><p>接下来让我们通过一个例子来了解具体的操作，重新回顾一下 Swift Package 的目录结构，我们会在 Sources 文件夹里看到与 target 同名的目录，目录里会存放着这个 target 的所有代码文件：</p><p><img src="/images/Screen%20Shot%202020-06-28%20at%2021.46.34.webp" alt="Screen Shot 2020-06-28 at 21.46.34"></p><blockquote><p>更具体的规则大家可以查看 <a href="https://developer.apple.com/videos/play/wwdc2019/408/">Adopting Swift Packages in Xcode - WWDC19</a>。</p></blockquote><p>接着我们来看 GameLogic 目录的结构，下面我们可以看到 storyboard 和 xcassets 不需要做任何的额外声明，直接添加到对应的目录里即可。</p><p>但 Internal Notes.txt 有些特别，它只是开发过程中使用的文档，我们不希望它也被打包到 Package 里，此时我们可以通过 <code>excludes</code> 参数进行指定：</p><p><img src="/images/Screen%20Shot%202020-06-28%20at%2021.26.42.webp" alt="Screen Shot 2020-06-28 at 21.26.42"></p><p>如果我们要添加一些运行时需要的资源文件，就可以通过 <code>resources</code> 参数进行指定，大部分资源文件都可以使用 <code>process</code> 指定，此时它们会根据对应的平台和打包方式自动进行处理：</p><p><img src="/images/15933524748194.webp" alt="-w1437"></p><p>有时我们需要目录在打包到 Package 之后也能保持它的结构，那么此时就可以使用 <code>copy</code> 选项：</p><p><img src="/images/Screen%20Shot%202020-06-28%20at%2021.56.16.webp" alt="Screen Shot 2020-06-28 at 21.56.16"></p><p><img src="/images/Screen%20Shot%202020-06-28%20at%2021.57.41.webp" alt="Screen Shot 2020-06-28 at 21.57.41"></p><p><code>.process</code> 选项会使用 SwiftPM 预设的规则自动进行处理：</p><ul><li>不需要特殊处理的话绝大部分场景都推荐使用它。</li><li>如果没有对应的规则处理的话，就会回退到 <code>.copy</code>。</li><li>这个选项会递归应用到目录下的所有文件。</li></ul><p><code>.copy</code> 没有规则，只是单纯的复制：</p><ul><li>可以用来覆盖预设的规则。</li><li>目录的复制会递归进行（深复制）。</li></ul><p><img src="/images/Screen%20Shot%202020-06-28%20at%2021.22.01.webp" alt="Screen Shot 2020-06-28 at 21.22.01"></p><h3 id="访问资源文件"><a href="#访问资源文件" class="headerlink" title="访问资源文件"></a>访问资源文件</h3><p>资源文件的访问会沿用现有的 <code>Foundation.Bundle</code>，这意味着 macOS 和 Linux 都可以共享同一套代码。</p><p>SwiftPM 会给所有带资源文件的 Target 自动合成当前 <code>Bundle</code> 的声明：</p><ul><li>Swift: <code>Bundle.module</code></li><li>Objective-C: <code>SWIFTPM_MODULE_BUNDLE</code></li></ul><p>访问资源文件：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Swift</span></span><br><span class="line"><span class="keyword">let</span> image <span class="operator">=</span> <span class="type">UIImage</span>(named: <span class="string">&quot;Logo&quot;</span>, in: <span class="type">Bundle</span>.module)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Objective-C</span></span><br><span class="line"><span class="type">UIImage</span> <span class="operator">*</span>image <span class="operator">=</span> [<span class="type">UIImage</span> imageNamed:@<span class="string">&quot;Logo&quot;</span> inBundle:<span class="type">SWIFTPM_MODULE_BUNDLE</span>];</span><br></pre></td></tr></table></figure><p>需要注意⚠️，<code>Bundle.module</code> 会被声明为 internal：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Bundle</span> &#123;</span><br><span class="line">    <span class="keyword">internal</span> <span class="keyword">var</span> module: <span class="type">Bundle</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果要让外部也能访问 Package 内的资源文件，推荐框架作者提供类型安全的外部接口。</p><h3 id="本地化"><a href="#本地化" class="headerlink" title="本地化"></a>本地化</h3><p>Package 内的资源文件也支持本地化，我们需要在 Package.swift 声明默认的语言 <code>defaultLocalization</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">package</span> <span class="operator">=</span> <span class="type">Package</span>(</span><br><span class="line">    name: <span class="string">&quot;DiceUI&quot;</span>,</span><br><span class="line">    defaultLocalization: <span class="string">&quot;en&quot;</span>,</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>存放资源的目录名称需要使用对应的 Language ID + Locale ID + <code>.lproj</code>，例如 <code>en.lproj</code>。</p><blockquote><p><a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html">Language and Locale ID - developer.apple.com</a></p></blockquote><p>本地化的资源通常是：</p><ul><li><code>.strings</code> 文件</li><li><code>.stringDicts</code> 文件</li><li>自定义资源文件</li></ul><p><img src="/images/Screen%20Shot%202020-06-29%20at%2008.39.21.webp" alt="Screen Shot 2020-06-29 at 08.39.21"></p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul><li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0271-package-manager-resources.md">SE-0271 Package Manager Resources</a></li><li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0272-swiftpm-binary-dependencies.md">SE-0272 Package Manager Binary Dependencies</a></li><li><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0278-package-manager-localized-resources.md">SE-0278 Package Manager Localized Resources</a></li><li><a href="https://artsy.github.io/blog/2018/12/21/why-is-swiftpm-taking-so-long/">Why is the Swift Package Manager taking so long?</a></li><li><a href="https://forums.swift.org/t/explicit-module-builds-the-new-swift-driver-and-swiftpm/36990">One More Thing</a></li></ul>]]>
    </content>
    <id>https://kemchenj.github.io/2020-06-29/</id>
    <link href="https://kemchenj.github.io/2020-06-29/"/>
    <published>2020-06-28T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>虽然 Swift Package Manager 发布到现在已经四年了，但采用率一直很低，无法完全替代 Cocoapods，不过今年情况将会有所改善，SwiftPM 今年迎来两个非常重要的功能：</p>
<ol>
<li>二进制依赖分发</li>
<li>资源文件</li>
</ol>]]>
    </summary>
    <title>WWDC 2020 - Swift Package Manager</title>
    <updated>2026-09-02T11:08:19.192Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<!--1. Swift 的 Index API 哪里繁琐？2. 为什么繁琐，它是怎么设计的？j3. 哪里可以改进，可以怎么改进？4. Swift 改进提案--><p>Swift 里的 <code>String</code> 繁琐难用的问题一直是大家频繁吐槽的点，趁着前两天 Swift 团队发了一份新的提案 <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0265-offset-indexing-and-slicing.md">SE-0265 Offset-Based Access to Indices, Elements, and Slices</a> 来改善 <code>String</code> 的使用，我想跟大家分享一下自己的理解。</p><p>SE-0265 提案的内容并不难理解，主要是增加 API 去简化几个 <code>Collection.subscript</code> 函数的使用，但这个提案的背景故事就比较多了，看了这篇提案我思考了很多关于这个 API 相关的内容，我认为问题的核心在于 <code>Collection.Index</code> 的设计。</p><span id="more"></span><p>在分析 <code>Collection.Index</code> 之前，我们先来看一下 <code>String</code> 常见的使用场景：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> str <span class="operator">=</span> <span class="string">&quot;String 的 Index 为什么这么难用？&quot;</span></span><br><span class="line"><span class="keyword">let</span> targetIndex <span class="operator">=</span> str.index(str.startIndex, offsetBy: <span class="number">4</span>)</span><br><span class="line">str[targetIndex]</span><br></pre></td></tr></table></figure><p>上面这段代码有几个地方容易让人产生疑惑：</p><ol><li>为什么 <code>targetIndex</code> 要调用 <code>String</code> 的实例方法去生成？</li><li>为什么这里需要使用 <code>str.startIndex</code>，而不是 <code>0</code>？</li><li>为什么 <code>String.Index</code> 使用了一个自定义类型，而不是直接使用 <code>Int</code>？</li></ol><p>上述的这些问题也造成了 String 的 API 变得十分繁琐，在其它语言里一行代码能解决的问题在 Swift 需要好几行，但这些其实都是 Swift 有意而为之的设计……</p><h2 id="不等长的元素"><a href="#不等长的元素" class="headerlink" title="不等长的元素"></a>不等长的元素</h2><p>在我们使用数组的时候，会有一个这样的假设：<strong>数组的每个元素都是等长的</strong>。例如在 C 里面，数组第 n 个元素的位置会是 <code>数组指针 + n * 元素长度</code>，这道公式可以让我们在 O(1) 的时间内获取到第 n 个元素。</p><p>但在 Swift 里这件事情并不一定成立，最好的例子就是 <code>String</code>，它的底层实现是 <strong>UTF-8 编码单位的集合</strong>，而暴露给外部的则是<strong>字符的集合</strong>，每个字符由 1 到 4 个 UTF-8 编码单位组成，换句话说，作为<strong>字符的集合</strong>时，实际上 <code>String</code> 的每一个元素的长度是不相等的。</p><p>这就意味着通过<strong>字符索引</strong>获取元素的时候，没办法简单地通过上面的公式计算出对应字符的位置，必须一直遍历到对应的元素才能获取到它的实际位置（UTF-8 编码单位的索引）。</p><p>那么问题就来了，如果要像 <code>Array</code> 那样直接使用 <code>Int</code> 作为索引的话，那迭代等操作就会产生更多的性能消耗，因为每次迭代都需要重新计算字符的偏移量：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 假设 String 是以 Int 作为 Index 的话</span></span><br><span class="line"><span class="comment">// 下面的代码复杂度将会是 O(n^2)</span></span><br><span class="line"><span class="comment">// O(1) + O(2) + ... + O(n) = O(n!) ~= O(n^2)</span></span><br><span class="line"><span class="keyword">let</span> hello <span class="operator">=</span> <span class="string">&quot;Hello&quot;</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="number">0</span><span class="operator">..&lt;</span>hello.count &#123;</span><br><span class="line">    <span class="built_in">print</span>(hello[i])</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="那-String-Index-是怎么设计的？"><a href="#那-String-Index-是怎么设计的？" class="headerlink" title="那 String.Index 是怎么设计的？"></a>那 String.Index 是怎么设计的？</h3><p>思路很简单，通过自定义 <code>Index</code> 类型，在内部记录对应元素的偏移量，迭代过程中复用它计算下一个 index 即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 下面的代码复杂度将会是 O(n)</span></span><br><span class="line"><span class="comment">// O(1) + O(1) + ... + O(1) = O(n)</span></span><br><span class="line"><span class="keyword">let</span> hello <span class="operator">=</span> <span class="string">&quot;Hello&quot;</span></span><br><span class="line"><span class="keyword">var</span> i <span class="operator">=</span> hello.startIndex</span><br><span class="line"><span class="keyword">while</span> i <span class="operator">!=</span> hello.endIndex &#123;</span><br><span class="line">    <span class="built_in">print</span>(hello[i])</span><br><span class="line">    hello.formIndex(after: <span class="operator">&amp;</span>i)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在<a href="https://github.com/apple/swift/blob/c829703d31e32f1e6d0222d0e16656dfea34d8e3/stdlib/public/core/StringIndex.swift#L15">源码</a>里我们可以找到 <code>String.Index</code> 的设计说明：</p><figure class="highlight mipsasm"><table><tr><td class="code"><pre><span class="line">String 的 Index 的内存布局如下:</span><br><span class="line"> </span><br><span class="line"> ┌──────────┬───────────────────╥────────────────┬──────────╥────────────────┐</span><br><span class="line"> │ <span class="keyword">b63:b16 </span> │      <span class="keyword">b15:b14 </span>     ║     <span class="keyword">b13:b8 </span>    │  <span class="keyword">b7:b1 </span>  ║       <span class="keyword">b0 </span>      │</span><br><span class="line"> ├──────────┼───────────────────╫────────────────┼──────────╫────────────────┤</span><br><span class="line"> │ position │ transcoded offset ║ grapheme <span class="keyword">cache </span>│ reserved ║ <span class="keyword">scalar </span>aligned │</span><br><span class="line"> └──────────┴───────────────────╨────────────────┴──────────╨────────────────┘</span><br><span class="line"></span><br><span class="line">- position aka `encodedOffset`: 一个 <span class="number">48</span> <span class="keyword">bit </span>值，用来记录码位偏移量</span><br><span class="line">- transcoded offset: 一个 <span class="number">2</span> <span class="keyword">bit </span>的值，用来记录字符使用的码位数量</span><br><span class="line">- grapheme <span class="keyword">cache: </span>一个 <span class="number">6</span> <span class="keyword">bit </span>的值，用来记录下一个字符的边界(?)</span><br><span class="line">- reserved: <span class="number">7</span> <span class="keyword">bit </span>的预留字段</span><br><span class="line">- <span class="keyword">scalar </span>aligned: 一个 <span class="number">1</span> <span class="keyword">bit </span>的值，用来记录标量是否已经对齐过(?)</span><br></pre></td></tr></table></figure><p>但由于 <code>Index</code> 里记录了码位的偏移量，而每个 <code>String</code> 的 <code>Index</code> 对应的偏移量都会有差异，所以 <strong><code>Index</code> 必须由 <code>String</code> 的实例生成</strong>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> str <span class="operator">=</span> <span class="string">&quot;C 语言&quot;</span></span><br><span class="line"><span class="keyword">let</span> index <span class="operator">=</span> str.index(str.startIndex, offsetBy: <span class="number">2</span>) <span class="comment">// 使用 String 的实例生成 index</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// |   C    |        |            语            |            言            |</span></span><br><span class="line"><span class="comment">// | U+0043 | U+0020 |          U+8BED          |          U+8A00          |</span></span><br><span class="line"><span class="comment">// |   43   |   20   |   E8   |   AF   |   AD   |   E8   |   A8   |   80   |</span></span><br><span class="line"><span class="comment">//                   ^</span></span><br><span class="line"><span class="comment">//              index 的位置</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// index.encodedOffset    == 2   “语”之前的字符使用的 UTF-8 编码单位数量</span></span><br><span class="line"><span class="comment">//                           总共有两个，“C” 使用了一个 43，“ ” 使用了一个 20</span></span><br><span class="line"><span class="comment">// index.transcodedOffset == 3   “语”由 E8 AF AD 三个 UTF-8 编码单位组成</span></span><br><span class="line"><span class="comment">// </span></span><br><span class="line"><span class="comment">// 换句话说 index 所代表的含义就是：</span></span><br><span class="line"><span class="comment">// 偏移 encodedOffset    个 UTF-8 编码单位</span></span><br><span class="line"><span class="comment">// 取   transcodedOffset 个 UTF-8 编码单位</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(str[index])  <span class="comment">// 语</span></span><br></pre></td></tr></table></figure><p>这种实现方式有趣的一点是，<code>Index</code> 使用过程中最消耗性能的是 <code>Index</code> 的生成，一旦 <code>Index</code> 生成了，使用它取值的操作复杂度都只会是 O(1)。</p><p>并且由于这种实现的特点，<strong>每个 <code>String</code> 的实例只应该使用自己生成的 <code>Index</code></strong> ，使用其它实例生成的 <code>Index</code> 会导致意外情况的发生：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> str2 <span class="operator">=</span> <span class="string">&quot;Clang&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// |   C    |   l    |   a    |   n    |   g    | </span></span><br><span class="line"><span class="comment">// | U+0043 | U+006C | U+0061 | U+006E | U+0067 | </span></span><br><span class="line"><span class="comment">// |   43   |   6C   |   61   |   6E   |   67   | </span></span><br><span class="line"><span class="comment">//                   ^</span></span><br><span class="line"><span class="comment">//              index 的位置</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">// 偏移了 2 个单位，取 3 个单位，所以这里会取到三个字符</span></span><br><span class="line"><span class="comment">// 但作为一个索引，理论上 index 只应该指向一个字符</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(str2[index]) <span class="comment">// ang</span></span><br></pre></td></tr></table></figure><p><code>Index</code> 在多个字符串间复用，就会造成这种一个索引会取到三个字符的意外情况，Swift 开发组表示过这属于一种未定义行为，在未来有可能会在运行时作为错误抛出。</p><h3 id="大费周章支持不等长的元素？"><a href="#大费周章支持不等长的元素？" class="headerlink" title="大费周章支持不等长的元素？"></a>大费周章支持不等长的元素？</h3><p>如果不需要让 <code>Collection</code> 去支持不等长的元素，那一切就会变得非常简单，<code>Collection</code> 不再需要 <code>Index</code> 这一层抽象，直接使用 <code>Int</code> 即可，并且在标准库的类型里元素不等长的集合类型也只有 <code>String</code>，对它进行特殊处理也是一种可行的方案。</p><p>摆在 Swift 开发组面前的是两个选择：</p><ul><li>继续完善 <code>Collection</code> 协议，让它更好地支持元素不等长的情况。</li><li>或者是专门给 <code>String</code> 建立一套机制，让它独立运行在 Collection 的体系之外。</li></ul><p>开发组在这件事情上的态度其实也有过摇摆：</p><ol><li>Swift 1 里 <code>String</code> 是遵循 <code>Collection</code> 的。</li><li>Swift 2~3 的时候移除了这个 Conformance，计划逐渐弃用掉 <code>Index</code> 这一层抽象直接使用 <code>Int</code>。</li><li>但在 Swift 4 之后又重新改了回去。</li></ol><p>这样做的好处主要还是保证 API 的正确性，提升代码的复用，之前在 Swift 2~3 里扩展一些集合相关的函数时，一模一样的代码需要在 <code>String</code> 和 <code>Collection</code> 里各写一套实现。</p><p>尽管我们确实需要 <code>Index</code> 这一层抽象去表达 <code>String</code> 这一类元素不等长的数组，但也不可否认它给 API 调用带来了一定程度负担。（Swift 更倾向于 API 的正确性，而不是易用性）</p><h2 id="Index-不一定从-0-开始"><a href="#Index-不一定从-0-开始" class="headerlink" title="Index 不一定从 0 开始"></a>Index 不一定从 0 开始</h2><p>在使用一部分切片集合的时候，例如 <code>ArraySlice</code> 在使用 <code>Index</code> 取值时，大家也许会发现一些意料之外的行为，例如说：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a <span class="operator">=</span> [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>]</span><br><span class="line"><span class="keyword">let</span> b <span class="operator">=</span> a[<span class="number">1</span><span class="operator">...</span><span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(b[<span class="number">1</span>]) <span class="comment">// 1</span></span><br></pre></td></tr></table></figure><p>这里我们预想的结果应该是 <code>2</code> 而不是 <code>1</code>，原因是我们在调用 <code>b[1]</code> 时有一个预设：<strong>所有集合的下标都是从 0 开始的</strong>。但对于 Swift 里的集合类型来说，这件事情并<strong>不成立</strong>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="built_in">print</span>(b.startIndex)          <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">print</span>((<span class="number">10</span><span class="operator">..&lt;</span><span class="number">100</span>).startIndex) <span class="comment">// 10</span></span><br></pre></td></tr></table></figure><h3 id="Collection-Index-是绝对索引"><a href="#Collection-Index-是绝对索引" class="headerlink" title="Collection.Index 是绝对索引"></a>Collection.Index 是绝对索引</h3><p>换句话说，<code>Collection</code> 里的 <code>Index</code> 其实是<strong>绝对索引</strong>，但对于我们来说，<code>Array</code> 和 <code>ArraySlice</code> 除了在生命周期处理时需要注意之外，其它 API 的调用都不会存在任何差异，也不应该存在差异，使用<strong>相对索引</strong>屏蔽掉数组和切片之间的差异应该是更好的选择，那还为什么要设计成现在的样子？</p><p>这个问题在论坛里有过很<a href="https://forums.swift.org/t/rant-indexing-into-arrayslice/14105">激烈的讨论</a>，核心开发组也只是出来简单地提了两句，大意是虽然对于用户来说确实不存在区别，但对于（标准库）集合类型的算法来说，基于现有的设计可以采取更加简单高效的实现，并且实现出来的算法也不存在 Index 必须为 Int 的限制。</p><p>我个人的理解是，对于 <code>Index == Int</code> 的 <code>Collection</code> 来说，<code>SubSequence</code> 的 <code>startIndex</code> 设为 0 确实很方便，但这也是最大的问题，任何以此为前提的代码都只对于 <code>Index == Int</code> 的 <code>Collection</code> 有效，对于 <code>Index != Int</code> 的 <code>Collection</code>，缺乏类似于 0 这样的常量来作为 <code>startIndex</code>，很难在抽象层面去实现统一的集合算法。</p><h3 id="我们想要的是相对索引"><a href="#我们想要的是相对索引" class="headerlink" title="我们想要的是相对索引"></a>我们想要的是相对索引</h3><p>其实我们可以把当前的 <code>Index</code> 看作是 underlying collection 的绝对索引，我们想要的不是 0-based collection 而是相对索引，但相对索引最终还是要转换成绝对索引才能获取到对应的数据，但这种相对索引意味着 API 在调用时要加一层索引的映射，并且在处理 <code>SubSequence</code> 的 <code>SubSequence</code> 这种嵌套调用时，想要避免多层索引映射带来的性能消耗也是需要额外的实现复杂度。</p><p>无论 Swift 之后是否会新增相对索引，它都需要基于绝对索引去实现，现在的问题只是绝对索引作为 API 首先被呈现出来，而我们在缺乏认知的情况下使用就会显得使用起来过于繁琐。</p><p>调整一下我们对于 <code>Collection</code> 抽象的认知，抛弃掉数组索引必定是 0 开头的想法，换成更加抽象化的 <code>startIndex</code>，这件事情就可以变得自然很多。引入抽象提升性能在 Swift 并不少见，例如说 <code>@escaping</code> 和 <code>weak</code>，习惯了之后其实也没那么糟糕。</p><h2 id="Index-之间的距离是-1，但也不是-1"><a href="#Index-之间的距离是-1，但也不是-1" class="headerlink" title="Index 之间的距离是 1，但也不是 1"></a>Index 之间的距离是 1，但也不是 1</h2><p>前面提到了 <code>Index == Int</code> 的 <code>Collection</code> 类型一定是从 0 开始，除此之外，由于 <code>Index</code> 偏移的逻辑也被抽象了出来，此时的 <code>Collection</code> 表现出来另一个特性 —— <strong>Index 之间的距离不一定是 “1”</strong> 。</p><p>假设我们要实现一个采样函数，每隔 n 个元素取一次数组的值：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Array</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">sample</span>(<span class="params">interval</span>: <span class="type">Int</span>, <span class="params">execute</span>: (<span class="type">Element</span>) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">        <span class="keyword">var</span> i <span class="operator">=</span> <span class="number">0</span></span><br><span class="line">        <span class="keyword">while</span> i <span class="operator">&lt;</span> count &#123;</span><br><span class="line">            execute(<span class="keyword">self</span>[i])</span><br><span class="line">            i <span class="operator">+=</span> interval</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">[<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>].sample(interval: <span class="number">2</span>) &#123;</span><br><span class="line">    <span class="built_in">print</span>(<span class="variable">$0</span>) <span class="comment">// 0, 2, 4, 6</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果我们想要让它变得更加泛用，让它能够适用于大部分集合类型，那么最好将它抽象成为一个类型，就像 Swift 标准库那些集合类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">SampleCollection</span>&lt;<span class="type">C</span>: <span class="type">RandomAccessCollection</span>&gt;: <span class="title class_ inherited__">RandomAccessCollection</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> storage: <span class="type">C</span></span><br><span class="line">    <span class="keyword">let</span> sampleInterval: <span class="type">Int</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">var</span> startIndex: <span class="type">C</span>.<span class="type">Index</span> &#123; storage.startIndex &#125;</span><br><span class="line">    <span class="keyword">var</span> endIndex: <span class="type">C</span>.<span class="type">Index</span> &#123; storage.endIndex &#125;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">index</span>(<span class="params">before</span> <span class="params">i</span>: <span class="type">C</span>.<span class="type">Index</span>) -&gt; <span class="type">C</span>.<span class="type">Index</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> i <span class="operator">==</span> endIndex &#123;</span><br><span class="line">            <span class="keyword">return</span> storage.index(endIndex, offsetBy: <span class="operator">-</span>storage.count.remainderReportingOverflow(dividingBy: sampleInterval).partialValue)</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> storage.index(i, offsetBy: <span class="operator">-</span>sampleInterval)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">index</span>(<span class="params">after</span> <span class="params">i</span>: <span class="type">C</span>.<span class="type">Index</span>) -&gt; <span class="type">C</span>.<span class="type">Index</span> &#123; storage.index(i, offsetBy: sampleInterval, limitedBy: endIndex) <span class="operator">??</span> endIndex &#125;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">distance</span>(<span class="params">from</span> <span class="params">start</span>: <span class="type">C</span>.<span class="type">Index</span>, <span class="params">to</span> <span class="params">end</span>: <span class="type">C</span>.<span class="type">Index</span>) -&gt; <span class="type">Int</span> &#123; storage.distance(from: start, to: end) <span class="operator">/</span> sampleInterval &#125;</span><br><span class="line">    <span class="keyword">subscript</span>(<span class="params">position</span>: <span class="type">C</span>.<span class="type">Index</span>) -&gt; <span class="type">C</span>.<span class="type">Element</span> &#123; storage[position] &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">init</span>(<span class="params">sampleInterval</span>: <span class="type">Int</span>, <span class="params">storage</span>: <span class="type">C</span>) &#123;</span><br><span class="line">        <span class="keyword">self</span>.sampleInterval <span class="operator">=</span> sampleInterval</span><br><span class="line">        <span class="keyword">self</span>.storage <span class="operator">=</span> storage</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>封装好了类型，那么我们可以像 <code>prefix</code> &#x2F; <code>suffix</code> 那样给对应的类型加上拓展方法，方便调用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">RandomAccessCollection</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">sample</span>(<span class="params">interval</span>: <span class="type">Int</span>) -&gt; <span class="type">SampleCollection</span>&lt;<span class="keyword">Self</span>&gt; &#123;</span><br><span class="line">        <span class="type">SampleCollection</span>(sampleInterval: interval, storage: <span class="keyword">self</span>)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> array <span class="operator">=</span> [<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>]</span><br><span class="line">array.sample(interval: <span class="number">2</span>).forEach &#123; <span class="built_in">print</span>(<span class="variable">$0</span>) &#125; <span class="comment">// 0, 2, 4, 6</span></span><br><span class="line">array.sample(interval: <span class="number">3</span>).forEach &#123; <span class="built_in">print</span>(<span class="variable">$0</span>) &#125; <span class="comment">// 0, 3, 6</span></span><br><span class="line">array.sample(interval: <span class="number">4</span>).forEach &#123; <span class="built_in">print</span>(<span class="variable">$0</span>) &#125; <span class="comment">// 0, 4</span></span><br></pre></td></tr></table></figure><p><code>SampleCollection</code> 通过实现那些 <code>Index</code> 相关的方法达到了采样的效果，<strong>这意味着 <code>Index</code> 的抽象其实是经由 <code>Collection</code> 诠释出来的概念，与 <code>Index</code> 本身并没有任何关系</strong>。</p><p>例如说两个 <code>Index</code> 之间的距离，0 跟 2 对于两个不同的集合类型来说，它们的 <code>distance</code> 其实是可以不同的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> sampled <span class="operator">=</span> array.sample(interval: <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> firstIdx <span class="operator">=</span> sampled.startIndex               <span class="comment">// 0</span></span><br><span class="line"><span class="keyword">let</span> secondIdx <span class="operator">=</span> sampled.index(after: firstIdx)  <span class="comment">// 2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> numericDistance <span class="operator">=</span> secondIdx <span class="operator">-</span> firstIdx.     <span class="comment">// 2</span></span><br><span class="line">array.distance(from: firstIdx, to: secondIdx)   <span class="comment">// 2</span></span><br><span class="line">sampled.distance(from: firstIdx, to: secondIdx) <span class="comment">// 1</span></span><br></pre></td></tr></table></figure><p>所以我们在使用 <code>Index == Int</code> 的集合时，想要获取集合的第二个元素，使用 <code>1</code> 作为下标取值是一种错误的行为：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">sampled[<span class="number">1</span>]         <span class="comment">// 1</span></span><br><span class="line">sampled[secondIdx] <span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p><code>Collection</code> 会使用自己的方式去诠释两个 <code>Index</code> 之间的距离，所以就算我们遇上了 <code>Index == Int</code> 的 <code>Collection</code>，直接使用 <code>Index</code> 进行递增递减也不是一种正确的行为，最好还是正视这一层泛型抽象，减少对于具体类型的依赖。</p><h2 id="越界时的处理"><a href="#越界时的处理" class="headerlink" title="越界时的处理"></a>越界时的处理</h2><p>Swift 一直称自己是类型安全的语言，早期移除了 C 的 for 循环，引入了大量“函数式”的 API 去避免数组越界发生，但在使用索引或者切片 API 时越界还是会直接导致崩溃，这种行为似乎并不符合 Swift 的“安全”理念。</p><p>社区里每隔一段时间就会有人提议过改为使用 <code>Optional</code> 的返回值，而不是直接崩溃，但这些建议都被打回，甚至在 <a href="https://github.com/apple/swift-evolution/blob/master/commonly_proposed.md#strings-characters-and-collection-types">Commonly Rejected Changes</a> 里有专门的一节叫大家不要再提这方面的建议（除非有特别充分的理由）。</p><p>那么<em>类型安全</em>意味着什么呢？<strong>Swift 所说的安全其实并非是指避免崩溃，而是避免未定义行为（Undefined Behavior）</strong>，例如说数组越界时读写到了数组之外的内存区域，此时 Swift 会<a href="https://forums.swift.org/t/stdlib-cleanup-callback-for-fatal-swift-errors/26977/6">更倾向于终止程序的运行，而不是处于一个内存数据错误的状态继续运行下去</a>。</p><p><strong>Swift 开发组认为，数组越界是一种逻辑上的错误</strong>，在早期的<a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151214/002446.html">邮件列表</a>里比较清楚地阐述过这一点：</p><blockquote><p>On Dec 14, 2015, at 6:13 PM, Brent Royal-Gordon via swift-evolution <swift-evolution at swift.org> wrote:</p><p>…有一个很类似的使用场景，<code>Dictionary</code> 在下标取值时返回了一个 <code>Optional</code> 值。你也许会认为这跟 <code>Array</code> 的行为非常不一致。让我换一个说法来表达这件认知，对于 <code>Dictionary</code>来说，当你使用一个 key set 之外的 key 来下标取值时，难道这不是一个程序员的失误吗？</p><p><code>Array</code> 和 <code>Dictionary</code> 的使用场景是存在差异的。</p><p>我认为 <code>Array</code> 下标取值 80% 的情况下，使用的 index 都是通过 <code>Array</code> 的实例间接或直接生成的，例如说 <code>0..&lt;array.count</code>，或者 <code>array.indices</code>，亦或者是从 <code>tableView(_:numberOfRowsInSection:)</code> 返回的 <code>array.count</code> 派生出来的 <code>array[indexPath.row]</code>。这跟 <code>Dictionary</code> 的使用场景是不一样的，通常它的 key 都是<strong>别的</strong>什么数据里取出来的，或者是你想要查找与其匹配的值。例如，你很少会直接使用 <code>array[2]</code> 或 <code>array[someRandomNumberFromSomewhere]</code>，但 <code>dictionary[“myKey”]</code> 或 <code>dictionary[someRandomValueFromSomewhere]</code> 却是非常常见的。</p><p>由于这种使用场景上的差异，所以 <code>Array</code> 通常会使用一个非 <code>Optional</code> 的下标 API，并且会在使用非法 index 时直接崩溃。而 <code>Dictionary</code> 则拥有一个 <code>Optional</code> 的下标 API，并且在 index 非法时直接返回 <code>nil</code>。</p></blockquote><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>核心开发团队先后有过两个草案改进 String 的 API，基本方向很明确，新增一种相对索引类型：</p><ol><li><strong>Collection 通用的索引类型</strong>。不需要考虑具体的 <code>Index</code> 类型，不需要根据数组实例去生成 <code>Index</code>，新的索引会在内部转换成 <code>Collection</code> 里的具体 <code>Index</code> 类型。</li><li><strong>简化 Index 的生成</strong>。</li><li><strong>subscript 返回 Optional 类型</strong>。</li></ol><p>具体的内容大家可以看提案，我是在第二份草案刚提出的时候开始写这篇文章的，删删改改终于写完了，现在草案已经变成了正式提案在 review 了，希望这篇文章可以帮助大家更好地理解这个提案的前因后果，也欢迎大家留言一起交流。</p><p>参考链接：</p><ul><li><a href="https://forums.swift.org/t/pitch-offset-indexing-and-slicing/28333">Offset Indexing and Slicing - Swift Forums</a></li><li><a href="https://forums.swift.org/t/string-essentials/21909">String Essentials - Swift Forms</a></li><li><a href="https://github.com/apple/swift/blob/master/docs/SequencesAndCollections.rst">swift&#x2F;SequencesAndCollections.rst at master</a></li><li><a href="https://github.com/apple/swift/blob/master/docs/StringDesign.rst">swift&#x2F;StringDesign.rst at master</a></li><li><a href="https://github.com/apple/swift/blob/master/docs/StringManifesto.md">swift&#x2F;StringManifesto.md at master</a></li><li><a href="https://www.swiftbysundell.com/podcast/46">46: “A desire for simplicity and performance”, with special guest Michael Ilseman – Swift by Sundell</a></li><li><a href="https://oleb.net/blog/2017/11/swift-4-strings/">Strings in Swift 4 - Ole Begemann</a></li><li><a href="https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871/32">Add Accessor With Bounds Check To Array - Swift Forums</a></li></ul>]]>
    </content>
    <id>https://kemchenj.github.io/2019-10-07/</id>
    <link href="https://kemchenj.github.io/2019-10-07/"/>
    <published>2019-10-06T16:00:00.000Z</published>
    <summary>
      <![CDATA[<!--
1. Swift 的 Index API 哪里繁琐？
2. 为什么繁琐，它是怎么设计的？j
3. 哪里可以改进，可以怎么改进？
4. Swift 改进提案
-->

<p>Swift 里的 <code>String</code> 繁琐难用的问题一直是大家频繁吐槽的点，趁着前两天 Swift 团队发了一份新的提案 <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0265-offset-indexing-and-slicing.md">SE-0265 Offset-Based Access to Indices, Elements, and Slices</a> 来改善 <code>String</code> 的使用，我想跟大家分享一下自己的理解。</p>
<p>SE-0265 提案的内容并不难理解，主要是增加 API 去简化几个 <code>Collection.subscript</code> 函数的使用，但这个提案的背景故事就比较多了，看了这篇提案我思考了很多关于这个 API 相关的内容，我认为问题的核心在于 <code>Collection.Index</code> 的设计。</p>]]>
    </summary>
    <title>Swift 的字符串为什么这么难用？</title>
    <updated>2026-09-02T11:08:19.192Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>SwiftUI 作为今年 WWDC 的重头戏，惊艳之余我们还需要关注一下它是如何与现有的 UIKit &#x2F; AppKit &#x2F; WatchKit 进行交互，以便我们能够在将来更平滑无缝地接入到已有的代码里。</p><p>这个 Session 的内容偏向于 API 的介绍，主要内容如下：</p><ul><li>与原生框架的交互<ul><li>原生页面里嵌入 SwiftUI</li><li>SwiftUI 里嵌入原生页面</li></ul></li><li>集成已有的数据模型</li><li>集成已有的系统功能<ul><li>Drag &amp; Drop</li><li>复制粘贴</li><li>焦点</li><li>Command</li><li>Undo &amp; Redo</li></ul></li><li>总结</li></ul><p>此外，这次苹果还推出了一系列的 SwiftUI 教程，<a href="https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit">其中一节</a>的内容讲的就是与 UIKit 的交互，推荐与本文一同阅读。</p><span id="more"></span><h2 id="与原生框架的交互"><a href="#与原生框架的交互" class="headerlink" title="与原生框架的交互"></a>与原生框架的交互</h2><p>与原生框架的交互主要是原生与 SwiftUI 页面的相互嵌套，由于 SwiftUI 的数据流设计，所以 SwiftUI 里嵌套原生页面涉及的 API 会比较多一些。</p><h3 id="原生页面里嵌入-SwiftUI"><a href="#原生页面里嵌入-SwiftUI" class="headerlink" title="原生页面里嵌入 SwiftUI"></a>原生页面里嵌入 SwiftUI</h3><p>将 SwiftUI 嵌入到 ViewController 里只需要套一层 HostingController 就可以了，以 UIKit 为例使用的就是 <code>UIHostingController</code>，它是 <code>UIViewController</code> 的子类，初始化时传入 SwiftUI 的 <code>View</code> 即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">UIHostingController</span>: <span class="title class_ inherited__">UIViewController</span> &#123;</span><br><span class="line">    <span class="keyword">init</span>(<span class="params">rootView</span>: <span class="type">View</span>) &#123; <span class="operator">...</span> &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>需要注意的是 WatchKit 里的 <code>WKHostingController</code> 略微有些不同，需要通过继承去完成这一个过程。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">WKHostingController</span>&lt;<span class="type">Body</span>: <span class="type">View</span>&gt;: <span class="title class_ inherited__">WKInterfaceController</span> </span><br></pre></td></tr></table></figure><h3 id="SwiftUI-里嵌入原生页面"><a href="#SwiftUI-里嵌入原生页面" class="headerlink" title="SwiftUI 里嵌入原生页面"></a>SwiftUI 里嵌入原生页面</h3><p>将 UIKit &#x2F; AppKit &#x2F; WatchKit 的 View &#x2F; ViewController 嵌入到 SwiftUI 中则需要使用一套 Representable 协议。</p><p>以 <code>UIView</code> 为例，与它对应的是 <code>UIViewRepresentable</code>，里面包含了四个生命周期方法：</p><ol><li><code>func makeCoordinator() -&gt; Coordinator</code>（可选）：在 View 创建前调用，用于创建 Coordinator。</li><li><code>func makeUIView(context:) -&gt; UIView</code>（必要）：在 View 创建时会被调用一次。</li><li><code>func updateUIView(_:context:)</code>（必要）：在 View 创建后会被立刻调用一次，随着数据更新会被反复调用。</li><li><code>func dismantleUIView(_:coordinator:)</code>（可选）：会在 View 被移除时调用。</li></ol><table><thead><tr><th align="center">协议</th><th align="center">创建</th><th align="center">更新</th><th align="center">销毁</th></tr></thead><tbody><tr><td align="center">UIView<br>Representable</td><td align="center">makeUIView<br>(context:)</td><td align="center">updateUIView<br>(_:context:)</td><td align="center">dismantleUIView<br>(_:coordinator:)</td></tr><tr><td align="center">UIViewController<br>Representable</td><td align="center">makeUIViewController<br>(context:)</td><td align="center">updateUIViewController<br>(_:context:)</td><td align="center">dismantleUIViewController<br>(_:coordinator:)</td></tr><tr><td align="center">NSView<br>Representable</td><td align="center">makeNSView<br>(context:)</td><td align="center">updateNSView<br>(_:context:)</td><td align="center">dismantleNSView<br>(_:coordinator:)</td></tr><tr><td align="center">NSViewController<br>Representable</td><td align="center">makeNSViewController<br>(context:)</td><td align="center">updateNSViewController<br>(_:context:)</td><td align="center">dismantleNSViewController<br>(_:coordinator:)</td></tr><tr><td align="center">WKInterfaceObject<br>Representable</td><td align="center">makeWKInterfaceObject<br>(context:)</td><td align="center">updateWKInterfaceObject<br>(_:context:)</td><td align="center">dismantleWKInterfaceObject<br>(_:coordinator:)</td></tr></tbody></table><p>那么我们该如何使用这一套 API 去完成常用的几个功能：</p><ul><li>Target-Action &#x2F; delegate 代理</li><li>响应 Environment 的变化</li><li>使用 SwiftUI 进行动画</li></ul><p>为了让 SwiftUI 与原生的 View 更好地交互，SwiftUI 提供了一个 RepresentableContext 协议，它包含了三个属性：</p><ul><li><code>Coordinator</code>：帮助协调原生 View 与 SwiftUI，实现 Target-Action 和 delegate 模式。</li><li><code>Environment</code>：帮助原生 View 读取 SwiftUI 的 Environment，提供布局方向和 size-class 等等。</li><li><code>Transaction</code>：让原生 View 获取到 SwiftUI 传入的动画属性。</li></ul><p>在这里我们通过一个简单的例子，将 UIKit 的 <code>UISlider</code> 封装到 SwiftUI 里：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UIKitSlider</span>: <span class="title class_ inherited__">UIViewRepresentable</span> &#123;</span><br><span class="line">    <span class="meta">@Binding</span> <span class="keyword">var</span> value: <span class="type">Int</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">makeUIView</span>(<span class="params">context</span>: <span class="type">Context</span>) -&gt; <span class="type">UISlider</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> control <span class="operator">=</span> <span class="type">UISlider</span>()</span><br><span class="line">        <span class="keyword">return</span> control</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">updateView</span>(<span class="keyword">_</span> <span class="params">uiView</span>: <span class="type">UISlider</span>, <span class="params">context</span>: <span class="type">Context</span>) &#123;</span><br><span class="line">        uiView.value <span class="operator">=</span> value</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用 Target-Action 的时候，苹果建议使用 Coordinator 来完成 View 与数据的交互，首先我们建立一个 Coordinator 对象来记录 value 的改变：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UIKitSlider</span> &#123;</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Coordinator</span>: <span class="title class_ inherited__">NSObject</span> &#123;</span><br><span class="line">        <span class="meta">@Binding</span> <span class="keyword">var</span> value: <span class="type">Float</span></span><br><span class="line">        </span><br><span class="line">        <span class="keyword">init</span>(<span class="params">value</span>: <span class="type">Binding</span>&lt;<span class="type">Float</span>&gt;) &#123;</span><br><span class="line">            <span class="keyword">self</span>.<span class="variable">$value</span> <span class="operator">=</span>value</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">@objc</span> <span class="keyword">func</span> <span class="title function_">valueChanged</span>(<span class="keyword">_</span> <span class="params">sender</span>: <span class="type">UISlider</span>) &#123;</span><br><span class="line">            <span class="keyword">self</span>.value <span class="operator">=</span> sender.value</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为了保持数据的一致性，SwiftUI 推荐使用 <code>Binding</code> 类型来表达派生值（Derived Value），所以这里构造器传入的是 <code>Binding&lt;Float&gt;</code>，这里不做过多解释，具体的内容请看 <a href="https://developer.apple.com/wwdc19/226">Session 226 - SwiftUI 里的数据流</a>。</p><p>最后我们在 <code>makeCoordinator</code> 方法里创建 <code>Coordinator</code>，在 <code>makeUIView</code> 方法里通过 <code>context</code> 获取到 <code>Coordinator</code> 进行 Target-Action 的绑定：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">UIKitSlider</span>: <span class="title class_ inherited__">UIViewRepresentable</span> &#123;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">makeCoordinator</span>() -&gt; <span class="type">Coordinator</span> &#123; </span><br><span class="line">        <span class="keyword">return</span> <span class="type">Coordinator</span>(value: <span class="variable">$value</span>)</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">makeUIView</span>(<span class="params">context</span>: <span class="type">Context</span>) -&gt; <span class="type">UISlider</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> slider <span class="operator">=</span> <span class="type">UISlider</span>()</span><br><span class="line">        slider.addTarget(</span><br><span class="line">            context.coordinator,</span><br><span class="line">            action: #seletor(<span class="type">Coordinator</span>.ratingChanged),</span><br><span class="line">            for: .valueChanged</span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">return</span> slider </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="集成数据模型"><a href="#集成数据模型" class="headerlink" title="集成数据模型"></a>集成数据模型</h2><p>SwiftUI 内部的数据流管理非常直观易用，但我们也需要接入数据库等外部数据，这时候我们需要某种机制来让它们绑定到一起。</p><p>SwiftUI 提供了一个 <code>BindableObject</code> 协议来实现这部分功能，协议的实现要求非常简单，只有一个必须实现的 <code>didChange</code> 属性，在每次数据产生变动后让 <code>didChange</code> 发出一个信号即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">DataModel</span>: <span class="title class_ inherited__">BindableObject</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> didChange <span class="operator">=</span> <span class="type">PassthroughSubject</span>&lt;<span class="type">Void</span>, <span class="type">Never</span>&gt;()</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">var</span> userData: <span class="type">UserData</span> &#123;</span><br><span class="line">        <span class="keyword">didSet</span> &#123;</span><br><span class="line">            didChange.send()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>另外，在 View 里使用 <code>BindableObject</code> 的时候需要使用 <code>@ObjectBinding</code> 修饰：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">ArticleList</span>: <span class="title class_ inherited__">View</span> &#123;</span><br><span class="line">    <span class="meta">@ObjectBinding</span> <span class="keyword">var</span> data: <span class="type">DataModel</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">var</span> body: <span class="keyword">some</span> <span class="type">View</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样 SwiftUI 才能知道哪些数据是跟 View 绑定到一起的，在这些数据更新时让 View 也保持同步：</p><p><img src="/images/Screen%20Shot%202019-07-01%20at%2011.32.34.webp" alt="Screen Shot 2019-07-01 at 11.32.34"></p><h2 id="系统集成"><a href="#系统集成" class="headerlink" title="系统集成"></a>系统集成</h2><p>除了 UI 和数据之外，我们还需要与系统进行交互，SwiftUI 在这方面的 API 非常完备，之前系统里包含的功能都可以在 SwiftUI 里找到。</p><h3 id="Drag-Drop"><a href="#Drag-Drop" class="headerlink" title="Drag &amp; Drop"></a>Drag &amp; Drop</h3><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">View</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">onDrag</span>(</span><br><span class="line">        <span class="keyword">_</span> <span class="params">data</span>: <span class="keyword">@escaping</span> () -&gt; <span class="type">NSItemProvider</span></span><br><span class="line">    ) -&gt; <span class="keyword">some</span> <span class="type">View</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">onDrop</span>(</span><br><span class="line">        <span class="params">of</span> <span class="params">supportedTypes</span>: [<span class="type">String</span>],</span><br><span class="line">        <span class="params">delegate</span>: <span class="type">DropDelegate</span></span><br><span class="line">    ) -&gt; <span class="keyword">some</span> <span class="type">View</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">onDrop</span>(</span><br><span class="line">        <span class="params">of</span> <span class="params">supportedTypes</span>: [<span class="type">String</span>],</span><br><span class="line">        <span class="params">isTargeted</span>: <span class="type">Binding</span>&lt;<span class="type">Bool</span>&gt;?,</span><br><span class="line">        <span class="params">perform</span> <span class="params">action</span>: <span class="keyword">@escaping</span> ([<span class="type">NSItemProvider</span>], <span class="type">CGPoint</span>) -&gt; <span class="type">Bool</span></span><br><span class="line">    ) -&gt; <span class="keyword">some</span> <span class="type">View</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="复制粘贴"><a href="#复制粘贴" class="headerlink" title="复制粘贴"></a>复制粘贴</h3><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">View</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">onPaste</span>(</span><br><span class="line">        <span class="params">of</span> <span class="params">supportedTypes</span>: [<span class="type">String</span>],</span><br><span class="line">        <span class="params">perform</span> <span class="params">action</span>: <span class="keyword">@escaping</span> ([<span class="type">NSItemProvider</span>]) -&gt; <span class="type">Void</span></span><br><span class="line">    ) -&gt; <span class="keyword">some</span> <span class="type">View</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">onPaste</span>&lt;<span class="type">Payload</span>&gt;(</span><br><span class="line">        <span class="params">of</span> <span class="params">supportedTypes</span>: [<span class="type">String</span>],</span><br><span class="line">        <span class="params">validator</span>: <span class="keyword">@escaping</span> ([<span class="type">NSItemProvider</span>]) -&gt; <span class="type">Payload</span>?,</span><br><span class="line">        <span class="params">perform</span> <span class="params">action</span>: <span class="keyword">@escaping</span> (<span class="type">Payload</span>) -&gt; <span class="type">Void</span></span><br><span class="line">    ) -&gt; <span class="keyword">some</span> <span class="type">View</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="焦点"><a href="#焦点" class="headerlink" title="焦点"></a>焦点</h3><p>粘贴的操作与拖拽有一个区别，就是粘贴操作需要了解当前的焦点，把事件分发给当前焦点所在的 View 进行处理。</p><p>查找响应者的过程类似于 UIKit 里的 ResponderChain，从焦点所在的 View 向上查找响应事件的 View：</p><p><img src="/images/Screen%20Shot%202019-06-22%20at%2011.39.32.webp" alt="Screen Shot 2019-06-22 at 11.39.32"></p><p>SwiftUI 里大部分 View 默认都是无法成为焦点的，想要响应焦点事件的话，可以使用 <code>focusable</code> 这个 Modifier：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">View</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">focusable</span>(</span><br><span class="line">        <span class="keyword">_</span> <span class="params">isFocusable</span>: <span class="type">Bool</span>,</span><br><span class="line">        <span class="params">onFocusChange</span>: <span class="keyword">@escaping</span> (<span class="type">Bool</span>) -&gt; <span class="type">Void</span> <span class="operator">=</span> &#123; <span class="keyword">_</span> <span class="keyword">in</span> &#125;</span><br><span class="line">    ) -&gt; <span class="keyword">some</span> <span class="type">View</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Command"><a href="#Command" class="headerlink" title="Command"></a>Command</h3><p><code>onCommand</code> 是比较特殊的函数，它可以用来接收响应链上抛出的任何事件，例如菜单或者是 ToolBar 上的用户操作：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">View</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">onCommand</span>(</span><br><span class="line">        <span class="keyword">_</span> <span class="params">command</span>: <span class="type">Command</span>,</span><br><span class="line">         <span class="params">perform</span> <span class="params">action</span>: (() -&gt; <span class="type">Void</span>)<span class="operator">?</span></span><br><span class="line">     ) -&gt; <span class="keyword">some</span> <span class="type">View</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">Command</span>(<span class="keyword">#selector</span>(<span class="type">Object</span>.someOperation))</span><br></pre></td></tr></table></figure><p>除此之外还有 <code>onCommand</code> &#x2F; <code>onExit</code> &#x2F; <code>onPlayPause</code> 等等，这些基本的系统交互功能在 SwiftUI 上 都有对应的 API 能够使用。</p><h3 id="Undo-Redo"><a href="#Undo-Redo" class="headerlink" title="Undo &amp; Redo"></a>Undo &amp; Redo</h3><p>在 SwiftUI 里使用 <code>UndoManager</code> 跟以往一样，直接在数据层进行交互即可，但如果你需要在 View 里获取到当前的 <code>UndoManager</code>，你只需要从 <code>Environment</code> 里获取即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Environment</span>(\<span class="type">EnvironmentValues</span>.undoManager) <span class="keyword">var</span> undoManager</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2>]]>
    </content>
    <id>https://kemchenj.github.io/2019-07-14/</id>
    <link href="https://kemchenj.github.io/2019-07-14/"/>
    <published>2019-07-13T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>SwiftUI 作为今年 WWDC 的重头戏，惊艳之余我们还需要关注一下它是如何与现有的 UIKit &#x2F; AppKit &#x2F; WatchKit 进行交互，以便我们能够在将来更平滑无缝地接入到已有的代码里。</p>
<p>这个 Session 的内容偏向于 API 的介绍，主要内容如下：</p>
<ul>
<li>与原生框架的交互<ul>
<li>原生页面里嵌入 SwiftUI</li>
<li>SwiftUI 里嵌入原生页面</li>
</ul>
</li>
<li>集成已有的数据模型</li>
<li>集成已有的系统功能<ul>
<li>Drag &amp; Drop</li>
<li>复制粘贴</li>
<li>焦点</li>
<li>Command</li>
<li>Undo &amp; Redo</li>
</ul>
</li>
<li>总结</li>
</ul>
<p>此外，这次苹果还推出了一系列的 SwiftUI 教程，<a href="https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit">其中一节</a>的内容讲的就是与 UIKit 的交互，推荐与本文一同阅读。</p>]]>
    </summary>
    <title>WWDC 2019 - Integrating SwiftUI</title>
    <updated>2026-09-02T11:08:19.192Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>前几天 Cocoapods 1.7.0 正式版发布了，我最期待的一个功能是 Multiple Pod Projects，昨天顺手就给接入了，项目解析和索引效率有了非常明显的提升，过程中踩了些坑，这次一起把之前 debug 的经验分享一下。</p><span id="more"></span><h2 id="generate-multiple-pod-projects-选项"><a href="#generate-multiple-pod-projects-选项" class="headerlink" title="generate_multiple_pod_projects 选项"></a>generate_multiple_pod_projects 选项</h2><p>之前 Cocoapods 会把每个依赖作为 target 放到 Pods 项目里，但 xcodeproj 本身的编码不太能适应这种情况，在引入几十个 pod 的情况下，项目解析的效率会急剧下降。</p><p>以我公司其中一个主项目为例，Pods 项目的大小达到了 5.2 MB（这可都是纯文本），在第一次打开项目，解析项目构建索引时，就能明显听到风扇开始狂转，这个过程会持续好几分钟才会结束。</p><p>Cocoapods 这次更新引入了一个 generate_multiple_pod_projects 的选项，可以让每个依赖都作为一个单独的项目引入，大大增加了解析速度：</p><p><img src="/images/Screen%20Shot%202019-05-30%20at%2023.35.27.webp" alt="Screen Shot 2019-05-30 at 23.35.27"></p><p>开启的方式很简单，只要在 Podfile 里加入这一行就可以了：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">install! <span class="string">&#x27;cocoapods&#x27;</span>, <span class="symbol">generate_multiple_pod_projects:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>拆分后每个项目的大小都差不多是 40 - 100 kb 左右：</p><p><img src="/images/Screen%20Shot%202019-05-30%20at%2023.47.26.webp" alt="Screen Shot 2019-05-30 at 23.47.26"></p><p>这个选项开启之后的效果非常显著，我在 Xcode 里执行了 clean，之后 indexing 的过程在几秒钟里就结束了，而且风扇也没有狂转。</p><p>至于为什么这样可以提升项目的解析速度，我大概看了一下 xcodeproj 的编码，所有的 Item 都会按照类别存放到各自的 section 里，最终在项目的结构树里会以引用的形式呈现。</p><p>所以文件引用查找的范围是所有 Pod 引用库的文件的集合，而每次索引的构建都至少会遍历一次项目树，这就会导致索引时间的暴增，除此之外单个庞大的项目解析也不利于多线程执行，拆分成多个项目的话就能有效地解决这些问题。</p><h2 id="install-函数只能调用一次"><a href="#install-函数只能调用一次" class="headerlink" title="install! 函数只能调用一次"></a>install! 函数只能调用一次</h2><p>需要注意 <code>install!</code> 是个用来配置的函数，由于之前我还开启了另一个选项，所以接入时是这么做的：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">install! <span class="string">&#x27;cocoapods&#x27;</span>, <span class="symbol">generate_multiple_pod_projects:</span> <span class="literal">true</span></span><br><span class="line">install! <span class="string">&#x27;cocoapods&#x27;</span>, <span class="symbol">disable_input_output_paths:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>但是这么做之后发现不生效，后来才想起来 <code>install!</code> 是一个用来配置的函数，重复调用的话，只会以最后一次的调用为准。所以应该在一次调用里把它们都传入进去：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">install! <span class="string">&#x27;cocoapods&#x27;</span>, </span><br><span class="line">         <span class="symbol">disable_input_output_paths:</span> <span class="literal">true</span>,</span><br><span class="line">         <span class="symbol">generate_multiple_pod_projects:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><h2 id="Swift-版本控制"><a href="#Swift-版本控制" class="headerlink" title="Swift 版本控制"></a>Swift 版本控制</h2><p>另一个坑就是在 <code>post_install</code> 时，为了一些版本的兼容，需要遍历所有 target，调整一部分库的 Swift 版本：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">post_install <span class="keyword">do</span> |<span class="params">installer</span>|</span><br><span class="line"></span><br><span class="line">  swift_4_0_compatible = [ ... ]</span><br><span class="line">  swift_4_2_compatible = [ ... ]</span><br><span class="line"></span><br><span class="line">  installer.pod_targets.each <span class="keyword">do</span> |<span class="params">t</span>|</span><br><span class="line">    t.build_configurations.each <span class="keyword">do</span> |<span class="params">c</span>|</span><br><span class="line">      c.build_settings[<span class="string">&#x27;SWIFT_VERSION&#x27;</span>] = <span class="string">&#x27;4.0&#x27;</span> <span class="keyword">if</span> swift_4_0_compatible.<span class="keyword">include</span>? t.name</span><br><span class="line">      c.build_settings[<span class="string">&#x27;SWIFT_VERSION&#x27;</span>] = <span class="string">&#x27;4.2&#x27;</span> <span class="keyword">if</span> swift_4_2_compatible.<span class="keyword">include</span>? t.name</span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>但是如果开启了 <code>generate_multiple_pod_projects</code> 的话，由于项目结构的变化，<code>installer.pod_targets</code> 就没办法获得所有 pods 引入的 target 了。</p><h3 id="Podfile-里的代码如何-debug"><a href="#Podfile-里的代码如何-debug" class="headerlink" title="Podfile 里的代码如何 debug"></a>Podfile 里的代码如何 debug</h3><p>查了 Xcodeproj 和 Cocoapods 的文档之后我都没有得到很好的解答，所以我就想用 xcodeproj 本身的接口去处理这件事情。</p><p>由于 Podfile 本质上是 Ruby 脚本，所以这里我通常会使用 Ruby 的 debugger 去操作，通过 Ruby 强大的自省能力，在 debugger 里进行尝试然后找到我们需要的接口，开始之前我们需要安装一个 Ruby 的工具，步骤如下：</p><ol><li>首先是安装 debugger <code>gem install pry</code></li><li>接着在 Podfile 的开头导入 <code>require &#39;pry&#39;</code></li><li>然后在我们想要插入断点的地方插入 <code>binding.pry</code> 语句就可以了</li></ol><h3 id="查找能用的接口"><a href="#查找能用的接口" class="headerlink" title="查找能用的接口"></a>查找能用的接口</h3><p>我在 post_install 里插入了断点，接着运行 <code>pod install</code>，就看到断点生效了：</p><p><img src="/images/Screen%20Shot%202019-05-31%20at%2001.05.57.webp" alt="Screen Shot 2019-05-31 at 01.05.57"></p><p>Ruby 的自省能力非常强大，而且 pry 也基于此做了很多实用的功能，在这里我直接输入了 <code>installer</code> 回车，就能看到它所有属性都被递归打印出来。</p><p>这里面我找了一下之后，发现一个文档里没有记录的属性，叫做 <code>pod_target_subprojects</code>，包含了所有 Pods 的项目，似乎可以满足我们的需求：</p><p><img src="/images/Screen%20Shot%202019-05-31%20at%2001.08.47.webp" alt="Screen Shot 2019-05-31 at 01.08.47"></p><p>接着 Ctrl + d 退出 pry，回到 Podfile 修改即可：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">post_install <span class="keyword">do</span> |<span class="params">installer</span>|</span><br><span class="line"></span><br><span class="line">  swift_4_0_compatible = [ ... ]</span><br><span class="line">  swift_4_2_compatible = [ ... ]</span><br><span class="line"></span><br><span class="line">  installer.pod_target_subprojects.flat_map &#123; |<span class="params">p</span>| p.targets &#125;.each <span class="keyword">do</span> |<span class="params">t</span>|</span><br><span class="line">    t.build_configurations.each <span class="keyword">do</span> |<span class="params">c</span>|</span><br><span class="line">      c.build_settings[<span class="string">&#x27;SWIFT_VERSION&#x27;</span>] = <span class="string">&#x27;4.0&#x27;</span> <span class="keyword">if</span> swift_4_0_compatible.<span class="keyword">include</span>? t.name</span><br><span class="line">      c.build_settings[<span class="string">&#x27;SWIFT_VERSION&#x27;</span>] = <span class="string">&#x27;4.2&#x27;</span> <span class="keyword">if</span> swift_4_2_compatible.<span class="keyword">include</span>? t.name</span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>最后 <code>pod install</code> 一下，打开 Xcode 查看对应的 target 的编译设置，确实有效。</p><p>这里介绍的 debug 方法在 fastlane 里也适用，非常建议大家在编写复杂脚本时先用 debugger 去提前踩坑。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>用惯了 Ruby 的 debug 方式之后，回到 LLDB 感觉开发体验瞬间差了很多😂。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2019-05-31/</id>
    <link href="https://kemchenj.github.io/2019-05-31/"/>
    <published>2019-05-30T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>前几天 Cocoapods 1.7.0 正式版发布了，我最期待的一个功能是 Multiple Pod Projects，昨天顺手就给接入了，项目解析和索引效率有了非常明显的提升，过程中踩了些坑，这次一起把之前 debug 的经验分享一下。</p>]]>
    </summary>
    <title>开启 Cocoapods 新选项，加快项目索引速度</title>
    <updated>2026-09-02T11:08:19.192Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md">SE-0244 Opaque Result Types</a> 提案前一段时间通过了 review 并且在 Swift 5.1 里完成了实现，我最早阅读这份提案的时候理解不是很透彻，今天比较仔细地读了这篇 <a href="https://forums.swift.org/t/improving-the-ui-of-generics/22814">Improving the UI of generics</a> 之后有了更多的认识，而且发现自己之前发的 <a href="https://twitter.com/kemchenj/status/1107125451632201728">tweet</a> 里有一些错误的认知，所以这里写篇文章，希望用<strong>最直白的方式解释清楚提案的内容</strong>，跟大家分享一下我自己的理解。</p><h2 id="Opaque-Result-Types"><a href="#Opaque-Result-Types" class="headerlink" title="Opaque Result Types?"></a>Opaque Result Types?</h2><p>用最最简单的一句话来介绍这个提案的内容，就是它能让<strong>被调用方</strong>选择泛型返回值的具体类型。</p><span id="more"></span><p>这是什么意思呢？让我们来看目前最常见的泛型函数声明：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Shape</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">func</span> <span class="title function_">generic</span>&lt;<span class="type">T</span>: <span class="type">Shape</span>&gt;() -&gt; <span class="type">T</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> x: <span class="type">Rectangle</span> <span class="operator">=</span> generic() <span class="comment">// type(of: x) == Rectangle, 调用者决定返回值类型</span></span><br></pre></td></tr></table></figure><p>这样的声明很好，但有时候我们不希望暴露出具体的返回类型，也不想让<strong>调用者</strong>去依赖具体的类型，之前我们可以直接使用泛型类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">generic</span>() -&gt; <span class="type">Shape</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> x <span class="operator">=</span> generic() <span class="comment">// type(of: x) == Shape</span></span><br></pre></td></tr></table></figure><p>虽然这样确实能达成我们的目的，但也会带来一些副作用，例如说<a href="https://www.jianshu.com/p/0d3db4422954">性能问题</a>，因为实际上 <code>generic</code> 返回的是一个实例的容器。</p><p>我们更希望的是能够像第一种声明那样，<strong>在编译时就确定返回值的具体类型</strong>，并且由<strong>被调用方</strong>去决定：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">reverseGeneric</span>() -&gt; <span class="keyword">some</span> <span class="type">Shape</span> &#123; <span class="keyword">return</span> <span class="type">Rectangle</span>(<span class="operator">...</span>)  &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> x <span class="operator">=</span> reverseGeneric() </span><br><span class="line"><span class="comment">// type(of: x) == Rectangle</span></span><br><span class="line"><span class="comment">// 并且 x 的类型根据 reverseGeneric 的具体实现决定</span></span><br></pre></td></tr></table></figure><p>通过引入 <code>some</code> 这个关键字去修饰返回值，就可以让<strong>被调用方</strong>选择具体的返回值类型，并且是在编译时确定下来的，这意味着我们不需要额外的容器去存放返回的实际值。</p><p>另外它还可以作为属性使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">someNumber</span>() -&gt; <span class="keyword">some</span> <span class="type">Numeric</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> number: <span class="keyword">some</span> <span class="type">Numeric</span> <span class="operator">=</span> someNumber()</span><br></pre></td></tr></table></figure><h2 id="它可以？"><a href="#它可以？" class="headerlink" title="它可以？"></a>它可以？</h2><p>在这里我们需要先确立一个条件，<strong>通过 some 修饰的类型，都会在编译时确定下来，可以简单地理解为被调用方负责传入的一个泛型参数</strong>，相关的功能和限制都是基于这个特性延伸出来的。</p><p>根据前面的条件，如果返回值使用了 <code>some</code> 修饰，编译器可以推导出两件很重要的事情：</p><ol><li><strong>同一个函数签名，返回值的类型肯定也只能是同一个具体类型</strong></li><li><strong>外部不能够依赖函数实现里使用的返回值具体类型</strong></li></ol><p>这意味着什么？在之前，两个遵循了 <code>Equatable</code> 的实例不能判断是否相等，因为我们并不知道它们具体的类型是否一样，但如果使用了 <code>some</code>，并且是由同一个函数返回的，那就完全不是问题了：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">randomNumber</span>() -&gt; <span class="keyword">some</span> <span class="type">Equatable</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> i: <span class="type">Int</span> <span class="operator">=</span> <span class="number">32</span></span><br><span class="line">    <span class="keyword">return</span> i</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> x <span class="operator">=</span> randomNumber()</span><br><span class="line"><span class="keyword">let</span> y <span class="operator">=</span> randomNumber()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在这里使用 == 是没问题的</span></span><br><span class="line"><span class="comment">// 因为 x 跟 y 都是由 randomNumber 返回的</span></span><br><span class="line"><span class="comment">// 所以它们的具体类型必然一致</span></span><br><span class="line"><span class="built_in">print</span>(x <span class="operator">==</span> y)</span><br></pre></td></tr></table></figure><p>但是控制返回值类型的是调用者，所以你不能够依赖被调用方的具体实现：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">randomNumber</span>() -&gt; <span class="keyword">some</span> <span class="type">Equatable</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> i: <span class="type">Int</span> <span class="operator">=</span> <span class="number">32</span></span><br><span class="line">    <span class="keyword">return</span> i</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> otherNumber: <span class="type">Int</span> <span class="operator">=</span> <span class="number">38</span></span><br><span class="line"><span class="keyword">var</span> x <span class="operator">=</span> randomNumber()</span><br><span class="line">x <span class="operator">=</span> otherNumber <span class="comment">// error</span></span><br></pre></td></tr></table></figure><p>虽然你知道 <code>randomEquatableNumber</code> 的实现里使用的是 <code>Int</code>，但具体的实现有可能随时被调整，所以你不能依赖它。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>在这里只是简单地介绍了一些比较核心的内容，想了解更多细节的朋友可以看提案。实际上这个提案只是整个泛型语法改进计划里的第一步，之后等我有了更加深入的了解再做更多分享。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2019-05-05/</id>
    <link href="https://kemchenj.github.io/2019-05-05/"/>
    <published>2019-05-04T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md">SE-0244 Opaque Result Types</a> 提案前一段时间通过了 review 并且在 Swift 5.1 里完成了实现，我最早阅读这份提案的时候理解不是很透彻，今天比较仔细地读了这篇 <a href="https://forums.swift.org/t/improving-the-ui-of-generics/22814">Improving the UI of generics</a> 之后有了更多的认识，而且发现自己之前发的 <a href="https://twitter.com/kemchenj/status/1107125451632201728">tweet</a> 里有一些错误的认知，所以这里写篇文章，希望用<strong>最直白的方式解释清楚提案的内容</strong>，跟大家分享一下我自己的理解。</p>
<h2 id="Opaque-Result-Types"><a href="#Opaque-Result-Types" class="headerlink" title="Opaque Result Types?"></a>Opaque Result Types?</h2><p>用最最简单的一句话来介绍这个提案的内容，就是它能让<strong>被调用方</strong>选择泛型返回值的具体类型。</p>]]>
    </summary>
    <title>泛型语法改进第一弹 —— Opaque Result Types</title>
    <updated>2026-09-02T11:08:19.192Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>之前 Joe 跟 Doug 提了一个草案，提议加入一个叫做 Property Delegate 的提案，昨天作为一份<a href="https://github.com/DougGregor/swift-evolution/blob/property-delegates/proposals/NNNN-property-delegates.md">预备提案</a>发了出来，我看了之后觉得特别兴奋，所以写了这篇文章跟大家分享一下。</p><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>我们在 Swift 定义属性的时候，有时候会需要它表现出更复杂的行为，例如线程安全（例子来源于 Objc.io 的<a href="https://www.objc.io/blog/2018/12/18/atomic-variables/">这篇文章</a>）：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">let</span> queue: <span class="type">DispatchQueue</span> <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">var</span> _count: <span class="type">Int</span> <span class="operator">=</span> <span class="number">3</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">var</span> count: <span class="type">Int</span> &#123;</span><br><span class="line">  <span class="keyword">get</span> &#123; <span class="keyword">return</span> queue.sync &#123; _count &#125; &#125;</span><br><span class="line">  <span class="keyword">set</span> &#123; queue.sync &#123; <span class="keyword">self</span>._count <span class="operator">=</span> newValue &#125; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但每次都需要写这么长的代码显得很多余，所以这次提案提议增加一个名为 Property Delegates 的功能来简化这些属性的声明，这个名字翻译过来就是<strong>属性代理</strong>。</p><p>有了这个功能之后，想要一个线程安全的属性时只要加上一个注解 <code>@Atomic</code> 即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Atomic</span> <span class="keyword">var</span> count: <span class="type">Int</span> <span class="operator">=</span> <span class="number">3</span></span><br></pre></td></tr></table></figure><span id="more"></span><p>编译器会自动将属性的声明展开成下面这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="variable">$count</span> <span class="operator">=</span> <span class="type">Atomic</span>&lt;<span class="type">Int</span>&gt;(initialValue: <span class="number">3</span>)</span><br><span class="line"><span class="keyword">var</span> count: <span class="type">Int</span> &#123;</span><br><span class="line">  <span class="keyword">get</span> &#123; <span class="keyword">return</span> <span class="variable">$count</span>.value &#125;</span><br><span class="line">  <span class="keyword">set</span> &#123; <span class="variable">$count</span>.value <span class="operator">=</span> newValue &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>$count</code> 其实就是 <code>count</code> 的属性代理，一切 get &#x2F; set 行为都会通过 <code>$count</code> 去实现，<code>$</code> 是属性代理特有的前缀，我们只要在属性名前面加上就可以访问到实际的属性代理：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line">counter.count  <span class="comment">// 类型是 Int</span></span><br><span class="line">counter.<span class="variable">$count</span> <span class="comment">// 类型是 Atomic&lt;Int&gt;</span></span><br></pre></td></tr></table></figure><p><code>Atomic</code> 其实是一个具体的类型，声明属性代理的方式很简单，只要满足下面的要求即可：</p><ol><li>声明类型时必须用 <code>@propertyDelegate</code> 标注</li><li>必须有一个可读写 <code>value</code> 属性</li></ol><p>这里我简单实现了一个 <code>Atomic</code> 类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@propertyDelegate</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Atomic</span>&lt;<span class="type">Value</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">let</span> queue <span class="operator">=</span> <span class="type">DispatchQueue</span>(label: <span class="string">&quot;Atomic serial queue&quot;</span>)</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">var</span> _value: <span class="type">Value</span></span><br><span class="line">    </span><br><span class="line">  <span class="keyword">init</span>(<span class="keyword">_</span> <span class="params">value</span>: <span class="type">Value</span>) &#123;</span><br><span class="line">    <span class="keyword">self</span>._value <span class="operator">=</span> value</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">var</span> value: <span class="type">Value</span> &#123;</span><br><span class="line">    <span class="keyword">get</span> &#123; <span class="keyword">return</span> queue.sync &#123; <span class="keyword">self</span>._value &#125; &#125;</span><br><span class="line">    <span class="keyword">set</span> &#123; queue.sync &#123; <span class="keyword">self</span>._value <span class="operator">=</span> newValue &#125; &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>BTW，这个功能是借鉴 <a href="https://www.kotlincn.net/docs/reference/delegated-properties.html">Kotlin</a> 的，语法有微妙的区别。</p><h2 id="拓展"><a href="#拓展" class="headerlink" title="拓展"></a>拓展</h2><p>除了 <code>Atomic</code> 之外，还有很多玩法，例如通过 <code>UserDefaults</code> 存取的值：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@propertyDelegate</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">UserDefaultValue</span>&lt;<span class="type">T</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">let</span> key: <span class="type">String</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">init</span>(<span class="params">key</span>: <span class="type">String</span> &#123;</span><br><span class="line">    <span class="keyword">self</span>.key <span class="operator">=</span> key</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="params">var</span> <span class="params">value</span>: <span class="type">T</span> &#123;</span><br><span class="line">    <span class="keyword">get</span> &#123; <span class="keyword">return</span> <span class="type">UserDefaults</span>.standard.object(forKey: key) <span class="keyword">as!</span> <span class="type">T</span> &#125;</span><br><span class="line">    <span class="keyword">set</span> &#123; <span class="type">UserDefaults</span>.standard.set(newValue, forKey: key) &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@UserDefaultValue</span>(key: <span class="string">&quot;count&quot;</span>) <span class="params">var</span> <span class="params">count</span>: <span class="type">Int</span></span><br></pre></td></tr></table></figure><p>甚至是 Swift 值类型使用的优化技巧 —— 写时复制：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Copyable</span>: <span class="title class_ inherited__">AnyObject</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">copy</span>() -&gt; <span class="keyword">Self</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@propertyDelegate</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">CopyOnWrite</span>&lt;<span class="type">Value</span>: <span class="type">Copyable</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">init</span>(<span class="params">initialValue</span>: <span class="type">Value</span>) &#123;</span><br><span class="line">    value <span class="operator">=</span> initialValue</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">private(set)</span> <span class="keyword">var</span> value: <span class="type">Value</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">var</span> storageValue: <span class="type">Value</span> &#123;</span><br><span class="line">    <span class="keyword">mutating</span> <span class="keyword">get</span> &#123;</span><br><span class="line">      <span class="keyword">if</span> <span class="operator">!</span><span class="built_in">isKnownUniquelyReferenced</span>(<span class="operator">&amp;</span>value) &#123;</span><br><span class="line">        value <span class="operator">=</span> value.copy()</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> value</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">set</span> &#123;</span><br><span class="line">      value <span class="operator">=</span> newValue</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@CopyOnWrite</span> <span class="keyword">var</span> array <span class="operator">=</span> <span class="type">NSMutableArray</span>&lt;<span class="type">Int</span>&gt;()</span><br></pre></td></tr></table></figure><p>目前 Swift 对于这些属性的高级行为支持是硬编码在语言里的，例如说 <code>lazy</code> 和 <code>@NSCopying</code>，更好的方式是将通过某种统一的功能形式来完成它们的功能，Property Delegate 就是这么一个角色。</p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>将属性的行为使用属性代理来处理，并且让它们的声明糅合到一起能够简化代码，但也会带来很多问题，在这里我列举其中一部分出来。</p><h3 id="原有的-lazy-没办法被完全替代"><a href="#原有的-lazy-没办法被完全替代" class="headerlink" title="原有的 lazy 没办法被完全替代"></a>原有的 lazy 没办法被完全替代</h3><p>Property Delegate 只是单纯的语法糖，我们可以把它看成是一种特殊的宏，让编译器把属性的声明进行了简单的展开，但它本质上并没有改变语言运作的方式，<strong>现有代码能做到的它也能做到，现有代码做不到的它也做不到</strong>。</p><p>最典型的就是前面提到的 <code>Lazy</code>，Property Delegate 目前的设计让它没办法完全替代现有的 <code>lazy</code> 声明，原因很简单 —— 它访问不到 <code>self</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">lazy</span> <span class="keyword">var</span> count <span class="operator">=</span> <span class="keyword">self</span>.previousCount</span><br><span class="line"><span class="meta">@Lazy</span> <span class="keyword">var</span> count <span class="operator">=</span> <span class="keyword">self</span>.previousCount <span class="comment">// 编译错误</span></span><br></pre></td></tr></table></figure><h3 id="访问级别"><a href="#访问级别" class="headerlink" title="访问级别"></a>访问级别</h3><p>语法如何融入到当前的设计里也是一个难点首先是访问级别，当我们声明一个延迟加载的属性时 <code>@Lazy var count: Int = 3</code>，我们希望 <code>count</code> 是 <code>open</code> 的，并且让属性代理 <code>$count</code> 隐藏起来，语法该怎么设计会更好？</p><p>目前提案给出的解决方案是让它们的访问级别保持一致，之后的版本可能会仿照 <code>set</code> 的访问级别的声明这样去处理：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Lazy</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">private</span>(storage) <span class="keyword">var</span> count: <span class="type">Int</span> <span class="operator">=</span> <span class="number">330</span></span><br></pre></td></tr></table></figure><p>但现阶段如果有需要的话，就还是用回之前的写法（不使用 Property Delegate）。</p><h3 id="组合属性代理"><a href="#组合属性代理" class="headerlink" title="组合属性代理"></a>组合属性代理</h3><p>我们可能会需要将属性代理组合起来使用：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Lazy</span></span><br><span class="line"><span class="meta">@Atomic</span></span><br><span class="line"><span class="keyword">var</span> object: <span class="type">NSObject</span> <span class="operator">=</span> <span class="operator">...</span></span><br></pre></td></tr></table></figure><p>直觉上我们会期望 object 既是线程安全的，又是写时复制的，当问题在于当前设计里的属性代理，代理处理的不只是 setter 和 getter，还有属性的存储，这也就导致了属性代理无法组合到一起，但前面提到 Property Delegate 只是单纯的语法糖，如果把这段代码展开，那它可能会是这样的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> object: <span class="type">NSObject</span> &#123;</span><br><span class="line">  <span class="keyword">get</span> &#123; <span class="keyword">return</span> <span class="variable">$object</span>.value &#125;</span><br><span class="line">  <span class="keyword">set</span> &#123; <span class="variable">$object</span>.value <span class="operator">=</span> newValue &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> <span class="variable">$object</span>: <span class="type">Atomic</span>&lt;<span class="type">NSObject</span>&gt; &#123;</span><br><span class="line">  <span class="keyword">get</span> &#123; <span class="keyword">return</span> $<span class="variable">$object</span>.value &#125;</span><br><span class="line">  <span class="keyword">set</span> &#123; $<span class="variable">$object</span>.value <span class="operator">=</span> value &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> $<span class="variable">$object</span>: <span class="type">Lazy</span>&lt;<span class="type">Atomic</span>&lt;<span class="type">NSObject</span>&gt;&gt;<span class="operator">=</span> <span class="operator">...</span> </span><br></pre></td></tr></table></figure><p>官方的解释是，这样的写法虽然在大部分情况下能够表现出我们期望的行为，但实际的语句含义与我们表达的并不相符，并且部分情况下可能会产生预期之外的行为，所以目前只支持单个属性代理。</p><p>BTW，这一部分官方其实写的比较含糊，我自己也没太理解什么情况下会出问题。</p><h3 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h3><p>其它一些细枝末节的东西也挺多的，例如是否应该使用 <code>$</code> 作为标示等，有人提议使用下划线，这样会更加符合现有代码库的做法，把 <code>$</code> 这个标示留下来给以后的功能使用。</p><p>还有什么命名一致性的问题，目前的注解有大写驼峰，也有小写驼峰，并且也没有一个统一的规则，虽然这不在这个提案的讨论范围内，但还是需要给之后的规则保留足够的制定空间。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>增强属性的声明其实早在 <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavior-decls.md#composing-behaviors">SE-0030</a> 提案里就有了，但因为底层设计没有稳定，而且优先级不高，所以这个提案虽然通过了，但一直没有实现，现在里面的设计已经不太符合现在的 Swift 了。</p><p>目前 Property Delegate 的提案还没有正式提交，正在讨论阶段，但其实功能本身的思路和优势是很清晰的，我个人认为这种功能肯定会被引入，讨论的重点主要在于语法的形式，如何与现有的语法结合的更好，如何适应之后自定义注解功能的加入。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2019-04-14/</id>
    <link href="https://kemchenj.github.io/2019-04-14/"/>
    <published>2019-04-13T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>之前 Joe 跟 Doug 提了一个草案，提议加入一个叫做 Property Delegate 的提案，昨天作为一份<a href="https://github.com/DougGregor/swift-evolution/blob/property-delegates/proposals/NNNN-property-delegates.md">预备提案</a>发了出来，我看了之后觉得特别兴奋，所以写了这篇文章跟大家分享一下。</p>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>我们在 Swift 定义属性的时候，有时候会需要它表现出更复杂的行为，例如线程安全（例子来源于 Objc.io 的<a href="https://www.objc.io/blog/2018/12/18/atomic-variables/">这篇文章</a>）：</p>
<figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">let</span> queue: <span class="type">DispatchQueue</span> <span class="operator">=</span> <span class="operator">...</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">var</span> _count: <span class="type">Int</span> <span class="operator">=</span> <span class="number">3</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">var</span> count: <span class="type">Int</span> &#123;</span><br><span class="line">  <span class="keyword">get</span> &#123; <span class="keyword">return</span> queue.sync &#123; _count &#125; &#125;</span><br><span class="line">  <span class="keyword">set</span> &#123; queue.sync &#123; <span class="keyword">self</span>._count <span class="operator">=</span> newValue &#125; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>但每次都需要写这么长的代码显得很多余，所以这次提案提议增加一个名为 Property Delegates 的功能来简化这些属性的声明，这个名字翻译过来就是<strong>属性代理</strong>。</p>
<p>有了这个功能之后，想要一个线程安全的属性时只要加上一个注解 <code>@Atomic</code> 即可：</p>
<figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Atomic</span> <span class="keyword">var</span> count: <span class="type">Int</span> <span class="operator">=</span> <span class="number">3</span></span><br></pre></td></tr></table></figure>]]>
    </summary>
    <title>抽象属性的行为 —— Property Delegates</title>
    <updated>2026-09-02T11:08:19.192Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>最近公司的项目开始做登录模块的页面改版，验证码&#x2F;邀请码的输入框没有找到很合适的第三方控件去做，自定义的时候踩了一些坑，所以写了这篇文章记录一下。</p><p>UI 大概是这样子的：</p><p><img src="/images/Screen%20Shot%202019-04-07%20at%2017.15.46.webp" alt="验证码输入框"></p><span id="more"></span><p><img src="/images/Screen%20Shot%202019-04-07%20at%2017.16.11.webp" alt="邀请码输入框"></p><h2 id="方案选择"><a href="#方案选择" class="headerlink" title="方案选择"></a>方案选择</h2><p>显示的部分基本上必须自定义，没办法直接使用 UITextView &#x2F; UITextField，实际动手之前我找了一下现有的方案：</p><ul><li>使用一个容器 View 去存放多个 UITextField &#x2F; UITextField<ul><li>优点：UI 的显示非常容易实现</li><li>缺点：与 TextField &#x2F; TextView 的交互会很多，复杂度高</li></ul></li><li>使用 UIView + UIKeyInput &#x2F; UITextInput 自定义控件<ul><li>优点：自由可控</li><li>缺点：只使用 UIKeyInput 的话需要自己绘制输入光标，并且没有快捷填充等功能，而使用 UITextInput 的话需要实现的方法太多</li></ul></li><li><a href="https://github.com/kolesa-team/ios_pinCodeTextField">使用 UITextField 以及 NSAttributedString.Key.kern 控制字间距</a><ul><li>优点：可以轻松实现快速填充</li><li>缺点：计算字间距太麻烦</li></ul></li></ul><p>最后的方案是继承 UITextField，因为 UITextField 本身提供了足够的方法能够去调整它的显示，所以这里直接隐藏掉它自身显示的内容，使用多个 Label 进行替代。</p><p>这是<a href="https://gist.github.com/kemchenj/bc51eb610059c49a26d08bdc73d4743b">具体的实现</a>以及最后的效果：</p><p><img src="/images/RPReplay_Final1554649768.2019-04-07%2023_18_40.gif" alt="效果图"></p><h2 id="遇到的问题和解决方案"><a href="#遇到的问题和解决方案" class="headerlink" title="遇到的问题和解决方案"></a>遇到的问题和解决方案</h2><p>方案敲定之后，需要解决的事情就很明显了：</p><ol><li>如何控制输入光标显示的位置？</li><li>如何控制插入点的位置？</li><li>如何控制复制&#x2F;粘贴&#x2F;剪切的行为？</li><li>如何限制输入的字数和字符集？</li><li>如何隐藏掉 TextField 原本的内容？</li><li>如何控制字符串的显示？</li></ol><h3 id="如何控制输入光标显示的位置？"><a href="#如何控制输入光标显示的位置？" class="headerlink" title="如何控制输入光标显示的位置？"></a>如何控制输入光标显示的位置？</h3><p>只要重写 <code>caretRect</code> 方法即可，<code>UITextField</code> 会根据这个方法返回的 frame 去绘制输入光标：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">caretRect</span>(<span class="params">for</span> <span class="params">position</span>: <span class="type">UITextPosition</span>) -&gt; <span class="type">CGRect</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><h3 id="如何控制插入点的位置？"><a href="#如何控制插入点的位置？" class="headerlink" title="如何控制插入点的位置？"></a>如何控制插入点的位置？</h3><p>每次点击 TextField 进入输入状态时，系统会自动根据点击的位置选择一个合适的插入点，但这里的输入框我们只要让它的插入点一直保持在最后即可，有这么几种情况会导致插入点的位置改变：</p><ol><li>进入输入状态时</li><li>重按调整光标位置</li><li>输入或删除字符</li></ol><p>原本我是打算在各个生命周期里去改 <code>selectedTextRange</code> 的，但后面我发现直接重写它的 setter 函数更加方便，在它每次改变时直接重置到文本的最后即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 任何调整选择范围的行为都会直接把 insert point 调到最后</span></span><br><span class="line"><span class="keyword">override</span> <span class="keyword">var</span> selectedTextRange: <span class="type">UITextRange</span>? &#123;</span><br><span class="line">    <span class="keyword">get</span> &#123; <span class="keyword">return</span> <span class="keyword">super</span>.selectedTextRange &#125;</span><br><span class="line">    <span class="keyword">set</span> &#123; <span class="keyword">super</span>.selectedTextRange <span class="operator">=</span> textRange(from: endOfDocument, to: endOfDocument) &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="如何限制复制-粘贴-剪切的行为？"><a href="#如何限制复制-粘贴-剪切的行为？" class="headerlink" title="如何限制复制&#x2F;粘贴&#x2F;剪切的行为？"></a>如何限制复制&#x2F;粘贴&#x2F;剪切的行为？</h3><p>验证码的输入框不太好支持剪切 &#x2F; 复制 &#x2F; 选择等功能，所以这里我们限制 TextField 只处理粘贴，重写 <code>canPerformAction</code> 方法即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">canPerformAction</span>(<span class="keyword">_</span> <span class="params">action</span>: <span class="type">Selector</span>, <span class="params">withSender</span> <span class="params">sender</span>: <span class="keyword">Any</span><span class="operator">?</span>) -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> action <span class="operator">==</span> <span class="keyword">#selector</span>(paste(<span class="keyword">_</span>:))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="如何限制输入的字数和字符集？"><a href="#如何限制输入的字数和字符集？" class="headerlink" title="如何限制输入的字数和字符集？"></a>如何限制输入的字数和字符集？</h3><p>这里核心思路主要是通过代理方法去拦截字符的修改，UITextField 的 <code>shouldChangeText(in:replacementText:)</code> 处于未知原因没有被调用，所以我这里只能通过 Delegate 来拦截字符的修改：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">textField</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">textField</span>: <span class="type">UITextField</span>,</span><br><span class="line">    <span class="params">shouldChangeCharactersIn</span> <span class="params">range</span>: <span class="type">NSRange</span>,</span><br><span class="line">    <span class="params">replacementString</span> <span class="params">string</span>: <span class="type">String</span></span><br><span class="line">) -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> newText <span class="operator">=</span> text</span><br><span class="line">        .map &#123; <span class="variable">$0</span> <span class="keyword">as</span> <span class="type">NSString</span> &#125;</span><br><span class="line">        .map &#123; <span class="variable">$0</span>.replacingCharacters(in: range, with: string) &#125;</span><br><span class="line">        .map(textPreprocess) <span class="operator">??</span> <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">let</span> newTextCharacterSet <span class="operator">=</span> <span class="type">CharacterSet</span>(charactersIn: newText)</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">let</span> isValidLength <span class="operator">=</span> newText.count <span class="operator">&lt;=</span> codeLength</span><br><span class="line">    <span class="keyword">let</span> isUsingValidCharacterSet <span class="operator">=</span> validCharacterSet.isSuperset(of: newTextCharacterSet)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> isValidLength, isUsingValidCharacterSet &#123;</span><br><span class="line">        textField.text <span class="operator">=</span> newText</span><br><span class="line">        sendActions(for: .editingChanged)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里的代码很简单，生成一个修改后的字符串，然后检验修改后的长度和字符集是否合法即可。</p><p>这里返回 <code>false</code> 是因为在第三方的输入法上，如果一次性输入多个字符的话只会在第一个字符插入时调用这个方法，所以这里只能返回 <code>false</code>，然后手动修改 <code>text</code> 属性，并且发送 <code>editingChanged</code> 的 action。</p><h3 id="如何隐藏掉-TextField-原本的内容？"><a href="#如何隐藏掉-TextField-原本的内容？" class="headerlink" title="如何隐藏掉 TextField 原本的内容？"></a>如何隐藏掉 TextField 原本的内容？</h3><p>隐藏文字：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">textRect</span>(<span class="params">forBounds</span> <span class="params">bounds</span>: <span class="type">CGRect</span>) -&gt; <span class="type">CGRect</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> .zero</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>隐藏占位文字：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">placeholderRect</span>(<span class="params">forBounds</span> <span class="params">bounds</span>: <span class="type">CGRect</span>) -&gt; <span class="type">CGRect</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> .zero</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>隐藏边框：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">borderRect</span>(<span class="params">forBounds</span> <span class="params">bounds</span>: <span class="type">CGRect</span>) -&gt; <span class="type">CGRect</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> .zero</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="如何控制字符串的显示？"><a href="#如何控制字符串的显示？" class="headerlink" title="如何控制字符串的显示？"></a>如何控制字符串的显示？</h3><p>这里我做得相对比较简单粗暴，直接继承 <code>UILabel</code> 写一个 <code>CharacterLabel</code>，声明一个 <code>update</code> 方法去更新 Label 的状态，包括了字符，当前是否为编辑状态，正在编辑的字符 Label 是否为自己，外部只需要继承 <code>CharacterLabel</code> 就可以控制显示出来的 UI 了。</p><p>接着只要在 <code>TextField</code> 初始化时传入一个工厂方法去生成即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CharacterLabel</span>: <span class="title class_ inherited__">UILabel</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> isEditing <span class="operator">=</span> <span class="literal">false</span></span><br><span class="line">    <span class="keyword">var</span> isFocusingCharacter <span class="operator">=</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">update</span>(<span class="params">character</span>: <span class="type">Character</span>?, <span class="params">isFocusingCharacter</span>: <span class="type">Bool</span>, <span class="params">isEditing</span>: <span class="type">Bool</span>) &#123;</span><br><span class="line">        <span class="keyword">self</span>.text <span class="operator">=</span> character.map &#123; <span class="type">String</span>(<span class="variable">$0</span>) &#125;</span><br><span class="line">        <span class="keyword">self</span>.isEditing <span class="operator">=</span> isEditing</span><br><span class="line">        <span class="keyword">self</span>.isFocusingCharacter <span class="operator">=</span> isFocusingCharacter</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CodeTextField</span>: <span class="title class_ inherited__">UITextField</span> &#123;</span><br><span class="line">    <span class="keyword">init</span>(</span><br><span class="line">        <span class="params">characterLabelGenerator</span>: () -&gt; <span class="type">CharacterLabel</span>,</span><br><span class="line">        <span class="operator">...</span></span><br><span class="line">    ) &#123; <span class="operator">...</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后再让 TextField 在合适的时机去更新这些 label：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">init</span>(<span class="operator">...</span>) &#123;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">    addTarget(<span class="keyword">self</span>, action: <span class="keyword">#selector</span>(updateLabels), for: .editingChanged)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">@objc</span> </span><br><span class="line"><span class="keyword">private</span> <span class="keyword">func</span> <span class="title function_">updateLabels</span>() &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">becomeFirstResponder</span>() -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">defer</span> &#123; updateLabels() &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">super</span>.becomeFirstResponder()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">resignFirstResponder</span>() -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">defer</span> &#123; updateLabels() &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">super</span>.resignFirstResponder()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">deleteBackward</span>() &#123;</span><br><span class="line">    <span class="keyword">defer</span> &#123; sendActions(for: .editingChanged) &#125;</span><br><span class="line">    <span class="keyword">super</span>.deleteBackward()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>网上找的很多方案都做了很重的实现，有的甚至自己绘制了一个输入光标，但其实仔细看看文档的话，原生的控件就已经提供了充足的接口让我们自定义了。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2019-04-07/</id>
    <link href="https://kemchenj.github.io/2019-04-07/"/>
    <published>2019-04-06T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>最近公司的项目开始做登录模块的页面改版，验证码&#x2F;邀请码的输入框没有找到很合适的第三方控件去做，自定义的时候踩了一些坑，所以写了这篇文章记录一下。</p>
<p>UI 大概是这样子的：</p>
<p><img src="/images/Screen%20Shot%202019-04-07%20at%2017.15.46.webp" alt="验证码输入框"></p>]]>
    </summary>
    <title>记一次验证码输入框的实现</title>
    <updated>2026-09-02T11:08:19.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>之前 fir 上传接口返回的数据里增加了一个 release_id，让我们可以直接跳转到对应 Build 的下载页面，但 GitLab 并不支持<a href="https://gitlab.com/gitlab-org/gitlab-ce/issues/27424">动态 Environment URL</a>。</p><p>为此 GitLab 提供了一个环境变量 <code>CI_ENVIRONMENT_SLUG</code>，可以作为合法的子域名使用，但这种方式并不利于我们接入第三方服务，例如 iOS 常用的测试分发平台 fir。</p><p>解决方法也很简单，使用一个中转服务器去存储 <code>CI_ENVIRONMENT_SLUG</code> 与下载地址的映射，然后通过 <code>CI_ENVIRONMENT_SLUG</code> 访问时查询对应的下载链接，重定向过去即可。</p><span id="more"></span><p>我顺手用 Sinatra + SQLite 实现了一套，首先是定义资源 <code>download_url</code>，添加 put方法新增下载链接：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">put <span class="string">&#x27;/download_url&#x27;</span> <span class="keyword">do</span></span><br><span class="line">  request.body.rewind</span><br><span class="line">  json = <span class="variable constant_">JSON</span>.parse request.body.read</span><br><span class="line"></span><br><span class="line">  slug = json[<span class="string">&#x27;ci_environment_slug&#x27;</span>]</span><br><span class="line">  release_id = json[<span class="string">&#x27;fir_release_id&#x27;</span>]</span><br><span class="line">  download_url = json[<span class="string">&#x27;fir_download_url&#x27;</span>]</span><br><span class="line"></span><br><span class="line">  pass <span class="keyword">unless</span> !slug.<span class="literal">nil</span>? |<span class="params"></span>| !slug.empty?</span><br><span class="line">  pass <span class="keyword">unless</span> !release_id.<span class="literal">nil</span>? |<span class="params"></span>| !release_id.empty?</span><br><span class="line">  pass <span class="keyword">unless</span> !download_url.<span class="literal">nil</span>? |<span class="params"></span>| !download_url.empty?</span><br><span class="line">  </span><br><span class="line">  guru_club_releases.insert(<span class="symbol">ci_environment_slug:</span> slug, </span><br><span class="line">                            <span class="symbol">fir_release_id:</span> release_id,</span><br><span class="line">                            <span class="symbol">fir_download_url:</span> download_url)</span><br><span class="line"></span><br><span class="line">  <span class="string">&#x27;Success&#x27;</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>接着是 get，通过 <code>ci_environment_slug</code> 获取资源：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">get <span class="string">&#x27;/download_url&#x27;</span> <span class="keyword">do</span></span><br><span class="line">  slug = params[<span class="string">&#x27;ci_environment_slug&#x27;</span>]</span><br><span class="line"></span><br><span class="line">  row = guru_club_releases.where(<span class="symbol">ci_environment_slug:</span> slug).first</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> row</span><br><span class="line">    download_url = row[<span class="symbol">:fir_download_url</span>]</span><br><span class="line">    release_id = row[<span class="symbol">:fir_release_id</span>]</span><br><span class="line">    redirect <span class="string">&quot;<span class="subst">#&#123;download_url&#125;</span>?release_id=<span class="subst">#&#123;release_id&#125;</span>&quot;</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    halt <span class="number">404</span>, <span class="string">&#x27;Could not find corespond release&#x27;</span></span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>这里有完整的 <a href="https://github.com/kemchenj/DynamicEnvironmentURL">Demo</a>。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2018-12-27/</id>
    <link href="https://kemchenj.github.io/2018-12-27/"/>
    <published>2018-12-26T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>之前 fir 上传接口返回的数据里增加了一个 release_id，让我们可以直接跳转到对应 Build 的下载页面，但 GitLab 并不支持<a href="https://gitlab.com/gitlab-org/gitlab-ce/issues/27424">动态 Environment URL</a>。</p>
<p>为此 GitLab 提供了一个环境变量 <code>CI_ENVIRONMENT_SLUG</code>，可以作为合法的子域名使用，但这种方式并不利于我们接入第三方服务，例如 iOS 常用的测试分发平台 fir。</p>
<p>解决方法也很简单，使用一个中转服务器去存储 <code>CI_ENVIRONMENT_SLUG</code> 与下载地址的映射，然后通过 <code>CI_ENVIRONMENT_SLUG</code> 访问时查询对应的下载链接，重定向过去即可。</p>]]>
    </summary>
    <title>GitLab 实现动态 Environment URL</title>
    <updated>2026-09-02T11:08:19.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>这两天偶然发现系统设置里 tableView deselectRow 的时机和效果都很特别，正常情况下我们的 deselect 操作都会在 didSelect 代理方法里执行，抑或者是更加细致一点，在 <code>viewDidAppear</code> 里完成。</p><p>但 iOS 原生的 App 说不，我还可以做得更好，这是系统设置里的效果：</p><span id="more"></span><p><img src="/images/%E5%9B%BE%E5%83%8F.gif" alt="图像"></p><p>侧滑返回时，deselect 动画会随着滑动手势的进度而改变，搜了一下，国内似乎没有太多相关的文章，并且我手头常用的几款软件都做到没有类似的效果。</p><p>搜了一下之后，发现国外的记录也很少，只有三篇文章记录了这个交互，其中写的比较详细的是这篇 <a href="http://mikeabdullah.net/thl-diary-17-clearsselectiononviewwillappear.html">The Hit List Diary #17 – clearsSelectionOnViewWillAppear</a>。</p><h2 id="转场动画的抽象-transitionCoordinator"><a href="#转场动画的抽象-transitionCoordinator" class="headerlink" title="转场动画的抽象 transitionCoordinator"></a>转场动画的抽象 transitionCoordinator</h2><p>这个交互其实是通过 <code>UIViewController</code> 的 <code>transitionCoordinator</code> 属性实现的，它的类型是 <code>UIViewControllerTransitionCoordinator</code>。</p><p>简单来说，它可以帮助我们在转场动画里加入一些自定义的动画，自定义动画的进度和生命周期会与转场动画保持一致，使用它可以达到更加自然和一致的转场效果，例如 push 动画里 navigationBar 背景颜色的变化，它提供了这几个方法供我们注册动画生命周期的回调：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">UIViewControllerTransitionCoordinator</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">animate</span>(</span><br><span class="line">        <span class="params">alongsideTransition</span> <span class="params">animation</span>: ((<span class="type">UIViewControllerTransitionCoordinatorContext</span>) -&gt; <span class="type">Void</span>)<span class="operator">?</span>, </span><br><span class="line">        <span class="params">completion</span>: ((<span class="type">UIViewControllerTransitionCoordinatorContext</span>) -&gt; <span class="type">Void</span>)<span class="operator">?</span> <span class="operator">=</span> <span class="literal">nil</span></span><br><span class="line">    ) -&gt; <span class="type">Bool</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">animateAlongsideTransition</span>(</span><br><span class="line">        <span class="params">in</span> <span class="params">view</span>: <span class="type">UIView</span>?,</span><br><span class="line">        <span class="params">animation</span>: ((<span class="type">UIViewControllerTransitionCoordinatorContext</span>) -&gt; <span class="type">Void</span>)<span class="operator">?</span>, </span><br><span class="line">        <span class="params">completion</span>: ((<span class="type">UIViewControllerTransitionCoordinatorContext</span>) -&gt; <span class="type">Void</span>)<span class="operator">?</span> <span class="operator">=</span> <span class="literal">nil</span></span><br><span class="line">    ) -&gt; <span class="type">Bool</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">notifyWhenInteractionChanges</span>(</span><br><span class="line">        <span class="keyword">_</span> <span class="params">handler</span>: <span class="keyword">@escaping</span> (<span class="type">UIViewControllerTransitionCoordinatorContext</span>) -&gt; <span class="type">Void</span></span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>推荐大家去看一下 <code>UIViewControllerTransitionCoordinator</code> 这个协议的<a href="https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioncoordinator">文档</a>，这里摘录一段我觉得比较有趣的描述：</p><blockquote><p>Using the transition coordinator to handle view hierarchy animations is preferred over making those same changes in the viewWillAppear(_:) or similar methods of your view controllers. The block<strong>s</strong> you register with the methods of this protocol are guaranteed to execute at the same time as the transition animations. More importantly, the transition coordinator provides important information about the state of the transition, such as whether it was cancelled, to your animation blocks through the UIViewControllerTransitionCoordinatorContext object.</p><p>比起 viewWillAppear 和其它相似的 ViewController 生命周期函数，我们更加推荐使用 transitionCoordinator 处理视图层级的动画。你注册的函数可以保证与转场动画同时执行。更重要的是，transitionCoordinator 通过 UIViewControllerTransitionCoordinatorContext 协议提供了转场动画的状态等重要信息，例如动画是否已被取消等。</p></blockquote><p>我由于最近业务的原因，第一个想起的就是 navigationBar，像是 <code>barTintColor</code> 这种属性就可以使用 <code>transitionCoordinator</code> 做到更加自然的动画转场。</p><h2 id="实现与封装"><a href="#实现与封装" class="headerlink" title="实现与封装"></a>实现与封装</h2><p>我看了别人的文章并且尝试其它集中方式之后，感觉 <code>transitionCoordinator</code> 获取的最佳时机应该是 <code>viewWillAppear</code>，实现的逻辑大概是这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">viewWillAppear</span>(<span class="keyword">_</span> <span class="params">animated</span>: <span class="type">Bool</span>) &#123;</span><br><span class="line">    <span class="keyword">super</span>.viewWillAppear(animated)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断是否有被选中的 Row</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">let</span> selectedIndexPath <span class="operator">=</span> tableView.indexPathForSelectedRow &#123;</span><br><span class="line">        <span class="comment">// 判断是否有 transitionCoordinator</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">let</span> coordinator <span class="operator">=</span> transitionCoordinator &#123;</span><br><span class="line">            <span class="comment">// 有的情况下，通过 coordinator 注册 animation block</span></span><br><span class="line">            coordinator.animate(</span><br><span class="line">                alongsideTransition: &#123; <span class="keyword">_</span> <span class="keyword">in</span></span><br><span class="line">                    <span class="keyword">self</span>.tableView.deselectRow(at: selectedIndexPath, animated: <span class="literal">true</span>)</span><br><span class="line">                &#125;,</span><br><span class="line">                completion: &#123; context <span class="keyword">in</span></span><br><span class="line">                    <span class="comment">// 如果转场动画被取消了，则需要让 tableView 回到被选中的状态</span></span><br><span class="line">                    <span class="keyword">guard</span> context.isCancelled <span class="keyword">else</span> &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">                    <span class="keyword">self</span>.tableView.selectRow(at: selectedIndexPath, animated: <span class="literal">true</span>, scrollPosition: .none)</span><br><span class="line">                &#125;</span><br><span class="line">            )</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 没有的情况下直接 deselect </span></span><br><span class="line">            tableView.deselectRow(at: selectedIndexPath, animated: animated)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果把 transitionCoordinator 单纯地看成是一个动画抽象（抛开转场），我们希望跟随动画完成的操作就是 deselect，那么就可以更进一步地把这个 deselect 的操作封装到 <code>UITableView</code> 的 extension 里：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">UITableView</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">func</span> <span class="title function_">deselectRowIfNeeded</span>(<span class="params">with</span> <span class="params">transitionCoordinator</span>: <span class="type">UIViewControllerTransitionCoordinator</span>?, <span class="params">animated</span>: <span class="type">Bool</span>) &#123;</span><br><span class="line">        <span class="keyword">guard</span> <span class="keyword">let</span> selectedIndexPath <span class="operator">=</span> selectRowAtIndexPath <span class="keyword">else</span> &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">    </span><br><span class="line">        <span class="keyword">guard</span> <span class="keyword">let</span> coordinator <span class="operator">=</span> transitionCoordinator <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">self</span>.deselectRow(at: selectedIndexPath, animated: animated)</span><br><span class="line">            <span class="keyword">return</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        coordinator.animate(</span><br><span class="line">            alongsideTransition: &#123; <span class="keyword">_</span> <span class="keyword">in</span></span><br><span class="line">                <span class="keyword">self</span>.deselectRow(at: selectedIndexPath, animated: <span class="literal">true</span>)</span><br><span class="line">            &#125;,</span><br><span class="line">            completion: &#123; context <span class="keyword">in</span></span><br><span class="line">                <span class="keyword">guard</span> context.isCancelled <span class="keyword">else</span> &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">                <span class="keyword">self</span>.selectRow(at: selectedIndexPath, animated: <span class="literal">false</span>, scrollPosition: .none)</span><br><span class="line">            &#125;</span><br><span class="line">        )</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接着只要在 <code>viewWillAppear</code> 里调用即可：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">override</span> <span class="keyword">func</span> <span class="title function_">viewWillAppear</span>(<span class="keyword">_</span> <span class="params">animated</span>: <span class="type">Bool</span>) &#123;</span><br><span class="line">    <span class="keyword">super</span>.viewWillAppear(animated)</span><br><span class="line"></span><br><span class="line">    tableView.deselectRowIfNeeded(with: transitionCoordinator, animated: <span class="literal">true</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果大家在项目里封装了自己的 TableViewController 并且规范使用的话，那要加入这个效果就很简单了。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>这是<a href="https://github.com/kemchenj/DeselectRowTheBestWay">完整的示例</a>。</p><p>参考链接：</p><ul><li><a href="https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioncoordinator">UIViewControllerTransitionCoordinator 官方文档</a></li><li><a href="http://mikeabdullah.net/thl-diary-17-clearsselectiononviewwillappear.html">The Hit List Diary #17 – clearsSelectionOnViewWillAppear</a></li><li><a href="https://www.jianshu.com/p/6ec14f6762e5">iOS-导航栏透明度和barTintColor渐变过渡</a></li></ul>]]>
    </content>
    <id>https://kemchenj.github.io/2018-12-24/</id>
    <link href="https://kemchenj.github.io/2018-12-24/"/>
    <published>2018-12-23T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这两天偶然发现系统设置里 tableView deselectRow 的时机和效果都很特别，正常情况下我们的 deselect 操作都会在 didSelect 代理方法里执行，抑或者是更加细致一点，在 <code>viewDidAppear</code> 里完成。</p>
<p>但 iOS 原生的 App 说不，我还可以做得更好，这是系统设置里的效果：</p>]]>
    </summary>
    <title>iOS 原生 App 是怎么 deselectRow 的？</title>
    <updated>2026-09-02T11:08:19.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<ul><li>提案: <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0206-hashable-enhancements.md">SE-0206</a></li><li>作者: <a href="https://github.com/lorentey">Karoy Lorentey</a>, <a href="https://github.com/regexident">Vincent Esche</a></li><li>审核人: <a href="https://github.com/jckarter">Joe Groff</a></li><li>状态: <strong>已实现 (Swift 4.2)</strong></li><li>决策记录: <a href="https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/115">Rationale</a></li><li>实现：<ul><li><a href="https://github.com/apple/swift/pull/14913">apple&#x2F;swift#14913</a> (standard library, underscored),</li><li><a href="https://github.com/apple/swift/pull/16009">apple&#x2F;swift#16009</a> (<code>Hasher</code> interface),</li><li><a href="https://github.com/apple/swift/pull/16073">apple&#x2F;swift#16073</a> (automatic synthesis, de-underscoring)</li></ul></li><li>上一个版本: <a href="https://github.com/apple/swift-evolution/blob/f5a020ec79cdb64fc8700af91b1a1ece2d2fb141/proposals/0206-hashable-enhancements.md">1</a></li></ul><!--*During the review process, add the following fields as needed:** Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/)* Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM)* Previous Proposal: [SE-XXXX](XXXX-filename.md)--><h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><ul><li><a href="#intro">简介</a></li><li><a href="#why">提案缘由</a><ul><li><a href="#status-quo">现状</a></li><li><a href="#universal-hashing">通用的哈希函数</a></li></ul></li><li><a href="#proposed-solution">解决方案</a><ul><li><a href="#hasher"><code>Hasher</code> 结构体</a></li><li><a href="#hash-into"><code>hash(into:)</code> 实现要求</a></li></ul></li><li><a href="#detailed-design">细节设计</a><ul><li><a href="#hasher-details"><code>Hasher</code></a></li><li><a href="#hashable-details"><code>Hashable</code></a></li></ul></li><li><a href="#source-compatibility">代码兼容性</a></li><li><a href="#abi">对于 ABI 稳定的影响</a></li><li><a href="#resilience">对于 API 稳定的影响</a></li><li><a href="#alternatives">其它弃用的方案</a><ul><li><a href="#leave-hashable-alone">保留 <code>Hashable</code> 原本的定义</a></li><li><a href="#new-protocol">定义一个新协议</a></li><li><a href="#generic-hasher">让 <code>Hasher</code> 成为一个协议，使得  <code>hash(into:)</code> 变成一个泛型函数</a></li><li><a href="#closure-hasher">把 <code>hash(into:)</code> 的参数改为闭包，而不是一个新的类型</a></li></ul></li></ul><span id="more"></span><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a><a name="intro">简介</a></h2><!--This proposal introduces a new `Hasher` type representing the standardlibrary's universal hash function, and it extends the `Hashable`protocol with a new `hash(into:)` requirement that expresses hashingin terms of `Hasher`. This new requirement is intended to replace theold `hashValue` property, which is deprecated.--><p>本提案计划引入一个新的类型 <code>Hasher</code> 来抽象标准库里的通用哈希函数，并且会相应地加入一个新的 <code>hash(into:)</code> 实现要求来扩展 <code>Hashable</code> 协议。<code>hashValue</code> 属性将会被弃用，并且使用这个新的实现要求来替换掉它。</p><!--Switching to `hash(into:)` moves the choice of a hash function out of`Hashable` implementations, and into the standard library. This makesit considerably easier to manually conform types to `Hashable` -- thetask of writing a custom implementation reduces to identifying theparts of a type that should contribute to the hash value.--><p>切换到 <code>hash(into:)</code> 之后意味着把哈希函数的选择从 <code>Hashable</code> 的实现里分离了出来，放到了标准库里。这让手动遵循 <code>Hashable</code> 变得更加简单，自定义实现的内容减少到只需要指定参与哈希的成员即可。</p><!--Standardizing on a single, high-quality hash function greatly improvesthe reliability of `Set` and `Dictionary`. The hash function can bespecially selected and tuned to work well with the hash tables used bythese collections, preventing hash collision patterns that would breakthe expected performance of common operations.--><p>使用单一，高质量的标准化哈希函数非常有助于提高 <code>Set</code> 和 <code>Dictionary</code> 的可靠性。这些集合可以使用特定的哈希函数并且针对哈希表进行微调，避免哈希碰撞导致性能的下降。</p><!--`Hasher` is a resilient struct, enabling future versions of thestandard library to improve the hash function without the need tochange (or even recompile) existing code that implements `Hashable`.Not baking any particular hash function into `Hashable` types isespecially important in case a weakness is found in the currentalgorithm.--><p><code>Hasher</code> 是一个 resilient 的结构体，后续版本的标准库可以在避免破坏当前 <code>Hashable</code> 实现的同时改进哈希函数（甚至无需重新编译）。把哈希函数从 <code>Hashable</code> 类型里剥离出来，在当前算法出现问题时显得特别重要。</p><p>Swift-evolution 讨论进程: <a href="https://forums.swift.org/t/combining-hashes/9082">Combining Hashes</a></p><h2 id="提案缘由"><a href="#提案缘由" class="headerlink" title="提案缘由"></a><a name="why">提案缘由</a></h2><!--The Swift Standard Library includes two general-purpose hashingcollections, `Set` and `Dictionary`. These collections are builtaround hash tables, whose performance is critically dependent on theexpected distribution of the elements stored in them, along with thequality of the hash function that is used to derive bucket indices forindividual elements.--><p>Swift 标准库包含了两个通用的哈希集合 —— <code>Set</code> 和 <code>Dictionary</code>。这两个集合是围绕哈希表构建的，但旧版本里他们的性能极度依赖于他们存储的元素，更准确的来说是每一个元素的哈希函数的质量。</p><!--With a good hash function, simple lookups, insertions and removalstake constant time on average. However, when the hash function isn'tcarefully chosen to suit the data, the expected time of suchoperations can become proportional to the number of elements stored inthe table. If the table is large enough, such a regression can easilylead to unacceptable performance. When they're overwhelmed with hashcollisions, applications and network services may stop processing newevents for prolonged periods of time; this can easily be enough tomake the app unresponsive or to bring down the service.--><p>使用一个好的哈希函数时，简单的查找，插入，删除操作都只需要常数时间即可完成。然而，如果没有为当前数据选择一个合适的哈希函数时，这些操作的期望时间就会变成线性时间。如果这个表足够大的话，很容易导致无法接受的性能。但它们被哈希碰撞淹没时，应用和网络服务也许会长期处于无法工作的状态；这会很容易导致 app 无响应或者服务器宕机。</p><h3 id="现状"><a href="#现状" class="headerlink" title="现状"></a><a name="status-quo">现状</a></h3><!--Since Swift version 1.0, `Hashable` has had a single requirement ontop of `Equatable`: the `hashValue` property. `hashValue` looksdeceptively simple, but implementing it is unreasonably hard: Not onlydo we need to decide which components of our type should be involvedin hashing, but we also have to come up with a way to somehow distillthese components down into a single integer value.  The API isessentially asking us to implement a new hash function, from scratch,every single time we conform to `Hashable`.--><p>自 Swift 1.0 之后，<code>Hashable</code> 就是 <code>Equatable</code> 的实现前提：<code>hashValue</code> 属性。<code>hashValue</code> 属性看起来很简单，但却非常难实现：我们不止需要决定类型的哪一部分需要参与哈希，而且还不得不想出一种方法去把这些输入的元素糅合到一起，输出一个整型。这个 API 本质上是要求我们每一次遵循 <code>Hashable</code>，都实现一个新的哈希函数。</p><!--Given adequate documentation, it is reasonable to expect that anexperienced programmer implementing a custom type would be able toidentify what parts need to be hashed. On the other hand, implementinga good hash function requires careful consideration and specialistknowledge. It is unreasonable to expect Swift programmers to investtime and effort to get this right for every `Hashable` type outthere.--><p>在文档完备时，我们相信一个经验丰富的工程师可以在实现自定义类型了解哪些部分需要参与哈希。但另一方面，实现一个足够好的哈希函数需要仔细的考究和特定的知识。期望一个 Swift 工程师去花时间和精力让每一个 <code>Hashable</code> 的类型都得到正确实现是不合理的。</p><!--For example, consider the code below, extracted directly from thedocumentation of Swift 4.1's `Hashable`. Is this a good implementationof `hashValue`?--><p>举个例子，让我们看下面的代码，这是从 Swift 4.1 的 <code>Hashable</code> 文档里直接摘录过来。请问这是一个好的 <code>hashValue</code> 的实现吗？</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">GridPoint</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> x: <span class="type">Int</span></span><br><span class="line">  <span class="keyword">var</span> y: <span class="type">Int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">GridPoint</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> x.hashValue <span class="operator">^</span> y.hashValue <span class="operator">&amp;*</span> <span class="number">16777619</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">static</span> <span class="keyword">func</span> <span class="title function_">==</span> (<span class="params">lhs</span>: <span class="type">GridPoint</span>, <span class="params">rhs</span>: <span class="type">GridPoint</span>) -&gt; <span class="type">Bool</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> lhs.x <span class="operator">==</span> rhs.x <span class="operator">&amp;&amp;</span> lhs.y <span class="operator">==</span> rhs.y</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The answer is that it depends; while the hash values it produces areperfectly fine if `x` and `y` are expected to be small integers comingfrom trusted sources, this hash function does have some undesirableproperties:--><p>答案是具体情况具体分析；如果 <code>x</code> 和 <code>y</code> 都是从受信任的来源里产出的小整型，那这个函数就很合适。但这个哈希函数有下面这么几个不好的地方：</p><!--1.  The clever bit manipulations make it hard to understand what the    code does, or how it works. For example, can you tell what makes    16777619 a better choice for the multiplier than, say, 16777618?    What are the precedence rules for `^` and `&*` again? What's with    the ampersand, anyway?        We just wanted to use `GridPoint` values as keys in a    `Dictionary`, but first, we need to spend a couple of hours    learning about bitwise operations, integer overflows and the    exciting properties of coprime numbers.    (For what it's worth, the magic constant used in the example above    is the same as the one used for the 32-bit version of the [FNV-1a]    hashing algorithm, which uses a similar (if a little more    complicated) method to distill arbitrary byte sequences down into    a single integer.)2.  It is trivially easy to construct an arbitrarily large set of    `GridPoint` values that aren't equal, but have the same hash    value. If the values come from an untrusted source, they may    sometimes be deliberately chosen to induce collisions.3. The hash function doesn't do a particularly great job at mixing up    the input data; the hash values it produces tend to form long    chains of sequential integer clusters. While these aren't as bad    as hash collisions, some hash table operations can slow down    drasticaly when such clusters are present. (In Swift 4.1, `Set`    and `Dictionary` use open addressing with linear probing, and they    have to do some clever postprocessing of hash values to get rid of    such patterns.)--><ol><li><p>这些位运算符很难直观得反映出代码的意图或者是其工作原理。例如，你能说出为什么 16777619 是比 16777618 更好的选择嘛？<code>^</code> 和 <code>&amp;*</code> 的运算符哪一个优先级更高？这里的 <code>&amp;</code> 代表了什么？</p><p> 我们只是想要在 <code>Dictionary</code> 里使用 <code>GridPoint</code>，但首先我们需要花时间去学习位运算符，整数移除，以及互素因子。</p><p> （真正有价值的是，上面的例子中的 magic constant 跟 32 位版本的 <a href="http://www.isthe.com/chongo/tech/comp/fnv/index.html">FNV-1a</a> 哈希算法使用的的是一样的，使用了同样的方式去在字节序列中提取整型值）</p></li><li><p>任意构建一个足够大的 <code>GridPoint</code> 集合都很容易导致不同的元素产生了同样的哈希值。而如果数据的来源不可信，那它们就有可能会恶意诱发哈希碰撞。</p></li><li><p>这个哈希函数并没有很好地将输入的数据糅合到一起；它更倾向于组合一串整数簇的长链。虽然这不像哈希碰撞那么糟糕，但一些哈希表的操作在簇已经被占用时性能会急剧下降。（在 Swift 4.1 里，<code>Set</code> 和 <code>Dictionary</code> 会使用线性探测，对哈希值进行智能预处理去避免这种状况）</p></li></ol><!--It seems desirable for the standard library to provide better guidancefor people implementing `hashValue`.--><p>这些都意味着标准库需要为大家提供一种更好的 <code>hashValue</code> 实现指引。</p><h3 id="通用哈希函数"><a href="#通用哈希函数" class="headerlink" title="通用哈希函数"></a><a name="universal-hashing">通用哈希函数</a></h3><!--With [SE-0185], Swift 4.1 introduced compiler support for automaticsynthesis of `Hashable` conformance for certain types. For example,the `GridPoint` struct above can be made to conform to `Hashable`without explicitly defining `hashValue` (or `==`):--><p>伴随着 <a href="0185-synthesize-equatable-hashable.md">SE-0185</a>，Swift 4.1 引入了 <code>Hashable</code> 实现自动合成的编译器支持。例如，前面提到的的 <code>GridPoint</code> 结构体就不需要显式地实现 <code>hashValue</code> 就可以遵循 <code>Hashable</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">GridPoint</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> x: <span class="type">Int</span></span><br><span class="line">  <span class="keyword">var</span> y: <span class="type">Int</span></span><br><span class="line">  </span><br><span class="line">  <span class="comment">// hashValue 和 == 实现都会由编译器自动生成</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--[SE-0185] did not specify a hash function to be used for suchconformances, leaving it as an implementation detail of the compilerand the standard library. Doing this well requires the use of a hashfunction that works equally well on any number of components,regardless of their expected distributions.--><p><a href="0185-synthesize-equatable-hashable.md">SE-0185</a> 并没有给这种自动实现指定一个哈希函数，而是把实现细节留给了编译器和标准库。为了更好得完成这件事，需要哈希函数使用在任意数量元素时的性能都一样优秀，无论它们离散预期是怎么样。</p><!--Luckily, this problem has occured in other contexts before, and thereis an extensive list of hash functions that have been designed forexactly such cases: [Foller-Noll-Vo][FNV-1a], [MurmurHash],[CityHash], [SipHash], and [HighwayHash] are just a small selection ofthese. The last two algorithms include some light cryptographicelements so that they provide a level of protection against deliberatehash collision attacks. This makes them a better choice forgeneral-purpose hashed collections like `Set` and `Dictionary`.--><p>幸运的是，这个问题之前在别的情境下发生过了，并且已经针对这些情况设计了出一系列函数：<a href="http://www.isthe.com/chongo/tech/comp/fnv/index.html">Foller-Noll-Vo</a>, <a href="https://github.com/aappleby/smhasher">MurmurHash</a>, <a href="https://github.com/google/cityhash">CityHash</a>, <a href="https://131002.net/siphash/">SipHash</a> 和 <a href="https://github.com/google/highwayhash">HighwayHash</a> 是其中一部分。最后两个算法还包含了一些密码学的元素在里面，可以提供一定级别的哈希碰撞攻击防护。这让他们成为 <code>Set</code> 和 <code>Dictionary</code> 更好的选择。</p><!--Since [SE-0185] required the standard library to implement ahigh-quality universal hash function, it seems like a good idea toexpose it as public API, so that manual `Hashable` implementations cantake advantage of it, too.--><p>由于 <a href="0185-synthesize-equatable-hashable.md">SE-0185</a> 要求标准库去实现一个高质量的通用哈希函数，那么顺便把它们作为 API 公开出来也是一件理所应当的事情，这也可以让手动实现 <code>Hashable</code> 变得更方便。</p><!--Universal hash functions work by maintaining some internal state --this can be as simple as a single 32/64-bit integer value (fore.g. [FNV-1a]), but it is usually much wider than that. For example,[SipHash] maintains a state of 256 bits, while [HighwayHash] uses 1024bits. --><p>一般的哈希函数工作原理都是通过维护一系列的内部状态去运作 —— 最简单的可以是 32&#x2F;64-bit 的整型数值（例如：<a href="http://www.isthe.com/chongo/tech/comp/fnv/index.html">FNV-1a</a>），但通常会比这个数据量更多。例如，<a href="https://131002.net/siphash/">SipHash</a> 维护了一个 256-bit 的状态，而 <a href="https://github.com/google/highwayhash">HighwayHash</a> 使用的是 1024-bit 的。</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a><a name="proposed-solution">解决方案</a></h2><!--We solve `Hashable`'s implementation problems in two parts. First, wemake the standard library's hash function public. Second, we replace`hashValue` with a requirement that is designed specifically toeliminate the guesswork from manual `Hashable` implementations.--><p>我们解决 <code>Hashable</code> 实现问题的方案分为两个部分。第一，我们把标准库的哈希函数作为 API 公开出来。第二我们把 <code>hashValue</code> 替换掉，换成一个让手动实现 <code>Hashable</code> 产出的结果更加可预测的 API。</p><h3 id="Hasher-结构体"><a href="#Hasher-结构体" class="headerlink" title="Hasher 结构体"></a><a name="hasher"><code>Hasher</code> 结构体</a></h3><!--We propose to expose the standard library's standard hash function as a new, public struct type, called `Hasher`. This new struct captures the state of the hash function, and provides the following operations:--><p>我们提议将标准库的哈希函数通过一个新的，公开的结构体类型 <code>Hasher</code> 暴露出来。这个新的结构体会捕获哈希函数的状态，并且提供以下的操作：</p><!--1. An initializer to create an empty state. To make hash values less   predictable, the standard hash function uses a per-execution random   seed, so that generated hash values will be different in each   execution of a Swift program.--><ol><li><p>一个创建空状态的构造器。为了让哈希值更加难以预测，每次程序初始化时生成一个随机数，程序的整个生命周期里都会把它作为哈希函数的种子值进行使用，以便让每次运行时产生的哈希值都不同。</p> <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Hasher</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">init</span>()  <span class="comment">// 每次运行程序都生成一个随机数，作为种子值使用</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><!--2. Operations to feed new bytes to the hash function, mixing them   into its state:-->   <ol start="2"><li><p>给哈希函数填充字节序列，并且把它们揉合到自己状态里的操作：</p> <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Hasher</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">combine</span>(<span class="params">bytes</span> <span class="params">buffer</span>: <span class="type">UnsafeRawBufferPointer</span>)</span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">combine</span>&lt;<span class="type">H</span>: <span class="type">Hashable</span>&gt;(<span class="keyword">_</span> <span class="params">value</span>: <span class="type">H</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure> <!-- `combine(bytes:)` is the most general operation, suitable for use when the bytes to be hashed are available as a single contiguous region in memory.  --><p> <code>combine(bytes:)</code> 是最通用的操作，当字节序列可以作为一段单独的连续内存区域被哈希时就可以使用。</p> <!-- `combine(_:)` is a convenience operation that can take any `Hashable` value; we expect this will be more frequently useful. (We'll see how this is implemented in the next section.) --><p> <code>combine(_:)</code> 是一个便利操作，能够直接传入任意 <code>Hashable</code> 的值；我们希望这个函数会被更频繁地使用到。（在下一个小节我们会介绍这是如何实现的）</p></li></ol><!--1. An operation to finalize the state, extracting the hash value from it.--><ol start="3"><li><p>一个提取哈希值，结束状态的操作，</p> <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Hasher</span> &#123;</span><br><span class="line">  <span class="keyword">public</span> __consuming <span class="keyword">func</span> <span class="title function_">finalize</span>() -&gt; <span class="type">Int</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ol><!--   Finalizing the hasher state consumes it; it is illegal to call   `combine` or `finalize` on a hasher you don't own, or on one that's   already been finalized.--><p>   结束 hasher 的状态；对不属于你的 hasher 调用 <code>combine</code> 或者 <code>finalize</code> 方法是非法的，或者对已经结束了状态的 hasher 也是一样的。</p><!--For example, here is how one may use `Hasher` as a standalone type:--><p>例如，这是 <code>Hasher</code> 的其中一种用法</p><!--```swiftvar hasher = Hasher()        // Initialize state, usually by random seedinghasher.combine(23)           // Mix in several integer's worth of byteshasher.combine(42)print(hasher.finalize())     // Finalize the state and return the hash```--><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> hasher <span class="operator">=</span> <span class="type">Hasher</span>()        <span class="comment">// 初始化状态，通常是由随机种子完成</span></span><br><span class="line">hasher.combine(<span class="number">23</span>)           <span class="comment">// 填充一些整型字节</span></span><br><span class="line">hasher.combine(<span class="number">42</span>)</span><br><span class="line"><span class="built_in">print</span>(hasher.finalize())     <span class="comment">// 结束状态并且返回哈希值</span></span><br></pre></td></tr></table></figure><!--Within the same execution of a Swift program, `Hasher`s are guaranteedto return the same hash value in `finalize()`, as long as they are fedthe exact same sequence of bytes. (Note that the order of `combine`operations matters; swapping the two integers above will produce acompletely different hash.)--><p>在同一个 Swift 程序的同一次运行中，<code>Hasher</code> 可以保证只要传入一样的字节序列，就会产出一样的哈希值。（注意：<code>combine</code> 操作的顺序会对结果产生影响；只要交换上面两个整型传入的顺序就会产生完全不同的哈希）</p><!--However, `Hasher` may generate entirely different hash values in otherexecutions, *even if it is fed the exact same byte sequence*. Thisrandomization is a critical feature, as it makes it much harder forpotential attackers to predict hash values. `Hashable` has always beendocumented to explicitly allow such nondeterminism:--><p>然而，<code>Hasher</code> 在另外一次运行里会产生完全不同的结果，<strong>就算传入的是一样的字节序列</strong>。这种随机性很关键，因为它让潜在的攻击者更难预测到哈希值。通常 <code>Hashable</code> 在文档里会被定义为非确定性的。</p><!--> - Important: Hash values are not guaranteed to be equal across>   different executions of your program. Do not save hash values to>   use in a future execution.>> <cite>-- `Hashable` documentation</cite>--><blockquote><p>注意：相同程序，在不同次运行中，不保证会产生相同的哈希值。所以不要把哈希值保留到下一次运行中去使用。</p><p><cite>– <code>Hashable</code> 文档</cite></p></blockquote><!--(Random seeding can be disabled by setting a special environmentvariable; see [Effect on ABI stability](#abi) for details.)--><p>（随机种子可以通过特定的环境变量进行关闭；详情请看<a href="#abi">对于 ABI 稳定的影响</a>。）</p><!--The choice of which hash function `Hasher` implements is animplementation detail of the standard library, and may change in anynew release. This includes the size and internal layout of `Hasher`itself. (The current implementation uses SipHash-1-3 with 320 bits ofstate.)--><p><code>Hasher</code> 对于哈希函数的选择取决于标准库的实现细节，并且在未来的版本里可能会进行修改。这包括了 <code>Hasher</code> 自身的大小和内存布局。（目前的实现使用了 320 bits 的 SipHash-1-3 状态）</p><h3 id="hash-into-实现要求"><a href="#hash-into-实现要求" class="headerlink" title="hash(into:) 实现要求"></a><a name="hash-into"><code>hash(into:)</code> 实现要求</a></h3><!--Introducing `Hasher` is a big improvement, but it's only half of thestory: `Hashable` itself needs to be updated to make better use of it.--><p>引入 <code>Hasher</code> 是一个巨大的提升，但还需要再进一步：我们需要调整 <code>Hashable</code> 才能更好地使用 <code>Hasher</code>。</p><!--We propose to change the `Hashable` protocol by adding a new`hash(into:)` requirement:--><p>我们提议给 <code>Hashable</code> 增加一个新的 <code>hash(into:)</code> 实现要求：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">Hashable</span>: <span class="title class_ inherited__">Equatable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--At the same time, we deprecate custom implementations of the`hashValue` property. (Please see the section on [Sourcecompatibility](#source-compatibility) on how we'll do this withoutbreaking code written for previous versions of Swift.) In some futurelanguage version, we intend to convert `hashValue` to an extensionmethod.--><p>同时，我们会将 <code>hashValue</code> 属性标记为弃用。（请看<a href="#source-compatibility">代码稳定</a>小节，了解我们如何在不破坏旧版 Swift 代码的同时做到这一点。）在未来的版本里，我们倾向于把 <code>hashValue</code> 转换为一个 extension 方法。</p><!--To make it easier to express `hash(into:)` in terms of `Hashable`components, `Hasher` provides a variant of `combine` that simply calls`hash(into:)` on the supplied value:--><p>为了让我们更容易地在 <code>Hashable</code> 里使用 <code>hash(into:)</code>，<code>Haser</code> 提供了一个 <code>combine</code> 的变体，唯一做的事情就是对传入的值调用 <code>hash(into:)</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Hasher</span> &#123;</span><br><span class="line">  <span class="keyword">@inlinable</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">combine</span>&lt;<span class="type">H</span>: <span class="type">Hashable</span>&gt;(<span class="keyword">_</span> <span class="params">value</span>: <span class="type">H</span>) &#123;</span><br><span class="line">    value.hash(into: <span class="operator">&amp;</span><span class="keyword">self</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This is purely for convenience; `hasher.combine(foo)` is slightlyeasier to type than `foo.hash(into: &hasher)`.--><p>这单纯只是为了方便；<code>hasher.combine(foo)</code> 比 <code>foo.hash(into: &amp;hasher)</code> 更容易敲出来。</p><!--At first glance, it may not be obvious why we need to replace`hashValue`. After all, `Hasher` can be used to take the guesswork outof its implementation:--><p>乍一看，我们似乎没有很充分的理由去替换掉 <code>hashValue</code>，但毕竟 <code>Hasher</code> 可以用来把离散预测的逻辑从 <code>Hashable</code> 的实现里剥离出来：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">GridPoint</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123; </span><br><span class="line">    <span class="keyword">var</span> hasher <span class="operator">=</span> <span class="type">Hasher</span>()</span><br><span class="line">    hasher.combine(x)</span><br><span class="line">    hasher.combine(y)</span><br><span class="line">    <span class="keyword">return</span> hasher.finalize()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--What's wrong with this? What makes `hash(into:)` so much better that'sworth the cost of a change to a basic protocol?--><p>这段代码有什么问题？是什么让 <code>hash(into:)</code> 比之前更好，以至于我们需要去修改一个基础的协议？</p><!--* **Better Discoverability** -- With `hashValue`, you need to know  about `Hasher` to make use of it: the API does not direct you to do  the right thing. Worse, you have to do extra busywork by manually  initializing and finalizing a `Hasher` instance directly in your  `hashValue` implementation. Compare the code above to the  `hash(into:)` implementation below:    This is nice and easy, with minimal boilerplate. `Hasher` is part of  the function signature; people who need to implement `hash(into:)`  are naturally guided to learn about it.--><ul><li><p><strong>更好的可发现性</strong> – 使用 <code>hashValue</code> 时，你需要去了解怎么使用 <code>Hasher</code>：这个 API 并不能引导你去正确地完成这件事。甚至你还需要每次实现 <code>hashValue</code> 时手动初始化和结束一个 <code>Hasher</code> 示例。对比上面 <code>hash(into:)</code> 的实现：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">GridPoint</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>) &#123;</span><br><span class="line">    hasher.combine(x)</span><br><span class="line">    hasher.combine(y)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  这段代码简单明了，没有任何多余的部分。<code>Hasher</code> 作为函数签名的一部分，让你实现 <code>hash(into:)</code> 时自然会知道怎么去做。</p></li></ul><!--* **Guaranteed Dispersion Quality** -- Keeping the existing  `hashValue` interface would mean that there was no way for `Set` and  `Dictionary` to guarantee that `Hashable` types produce hash values  with good enough dispersion. Therefore, these collections would need  to keep postprocessing hash values. We'd like to eliminate  postprocessing overhead for types that upgrade to `Hasher`.--><ul><li><strong>保证了离散度</strong> – 保留现有的 <code>hashValue</code> 接口意味着 <code>Set</code> 和 <code>Dictionary</code> 难以保证 <code>Hashable</code> 类型可以产生足够离散的哈希值。因此，这些集合还需要去存储预处理过的哈希值。所以我们想通过引入 <code>Hasher</code> 来避免存储这些预处理值。</li></ul><!--* **Hasher Customizability** -- `hash(into:)` moves the initialization  of `Hasher` out of `Hashable` types, and into hashing  collections. This allows us to customize `Hasher` to the needs of  each hashing data structure. For example, the stdlib could start  using a different seed value for every new `Set` and `Dictionary`  instance; this somewhat improves reliability by making hash values  even less predictable, but (probably more importantly), it  drastically improves the performance of some relatively common  operations involving [copying data between `Set`/`Dictionary`  instances][quadratic-copy].--><ul><li><strong>Hasher 可自定义性</strong> – <code>hash(into:)</code> 把 <code>Hasher</code> 的初始化从 <code>Hashable</code> 中剥离出来，放到了哈希的集合里。这让我们可以根据每一个哈希数据结构的需要去自定义 <code>Hasher</code>。例如，标准库可以使用给每一个 <code>Set</code> 和 <code>Dictionary</code> 实例使用不同的种子值；这种做法可以让哈希值更难以预测，提高了可靠性，但（或者更重要的是），它极大地提升了那些需要在 <a href="https://bugs.swift.org/browse/SR-3268"><code>Set</code> &#x2F; <code>Dictionary</code> 实例之间复制数据</a>的常用操作的性能。</li></ul><!--* **Better Performance** -- Similarly, `hash(into:)` moves the  finalization step out of `Hashable`. Finalization is a relatively  expensive operation; for example, in SipHash-1-3, it costs three  times as much as a single 64-bit `combine`. Repeating it for every  single component of a composite type would make hashing unreasonably  slow.--><ul><li><strong>更优秀的性能</strong> – 同样的，<code>hash(into:)</code> 把计算最终值的步骤从 <code>Hashable</code> 里剥离了出来。最终值计算是一个很消耗性能的操作；例如，在 SipHash-1-3 里，它等价于三次 64-bit 的 <code>combine</code> 操作。对复合类型里的每一个组件进行求值会让哈希求值过程变得非常慢。</li></ul><!--  For example, consider the `GridRectangle` type below:--><p>  例如，下面的 <code>GridRectangle</code> 类型：</p>  <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">GridRectangle</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> topLeft: <span class="type">GridPoint</span></span><br><span class="line">  <span class="keyword">let</span> bottomRight: <span class="type">GridPoint</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--  With `hashValue`, its `Hashable` implementation would look like this:--><p>  使用 <code>hashValue</code> 时，它的 <code>Hashable</code> 实现大概会是这样：</p>  <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">GridRectangle</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123; <span class="comment">// 性能差，不建议使用</span></span><br><span class="line">    <span class="keyword">var</span> hasher <span class="operator">=</span> <span class="type">Hasher</span>()</span><br><span class="line">    hasher.combine(bits: topLeft.hashValue) </span><br><span class="line">    hasher.combine(bits: bottomRight.hashValue)</span><br><span class="line">    <span class="keyword">return</span> hasher.finalize()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--  Both of the `hashValue` invocations above create and finalize  separate hashers. Assuming finalization takes three times as much  time as a single combine call (and generously assuming that initialization  is free) this takes 15 combines' worth of time:-->  <p>  每一个 <code>hashValue</code> 的求解都会创建各自的 hasher 并且求值。假设求值操作相当于三次 combine（并且假设初始化不消耗资源），那就需要 15 个 combine 的运行时间：</p><pre><code><figure class="highlight scss"><table><tr><td class="code"><pre><span class="line"><span class="number">1</span>   hasher<span class="selector-class">.combine</span>(topLeft.hashValue)</span><br><span class="line"><span class="number">1</span>       hasher<span class="selector-class">.combine</span>(topLeft.x)     (在 topLeft.hashValue 里)</span><br><span class="line"><span class="number">1</span>       hasher<span class="selector-class">.combine</span>(topLeft.y)</span><br><span class="line"><span class="number">3</span>       hasher<span class="selector-class">.finalize</span>()</span><br><span class="line"><span class="number">1</span>   hasher<span class="selector-class">.combine</span>(bottomRight.hashValue)</span><br><span class="line"><span class="number">1</span>       hasher<span class="selector-class">.combine</span>(bottomRight.x) (在 bottomRight.hashValue 里)</span><br><span class="line"><span class="number">1</span>       hasher<span class="selector-class">.combine</span>(bottomRight.y)</span><br><span class="line"><span class="number">3</span>       hasher<span class="selector-class">.finalize</span>()</span><br><span class="line"><span class="number">3</span>   hasher<span class="selector-class">.finalize</span>()</span><br><span class="line">---</span><br><span class="line"><span class="number">15</span></span><br></pre></td></tr></table></figure></code></pre><!--  Switching to `hash(into:)` gets us the following code:--><p>  而使用 <code>hash(into:)</code> 就会是下面这样：</p>  <figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">GridRegion</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>) &#123;</span><br><span class="line">    hasher.combine(topLeft)</span><br><span class="line">    hasher.combine(bottomRight)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--  This reduces the cost of hashing to just four combines and a single  finalization, which takes less than half the time of our original  approach:--><p>  这可以显著降低哈希的消耗，只需要四次 combine 和一次最终值求解即可，比起原来的方式减少了将近一半的消耗：</p>  <figure class="highlight asciidoc"><table><tr><td class="code"><pre><span class="line"><span class="code"> 1   hasher.combine(topLeft.x)      (在 topLeft.hash(into:) 里)</span></span><br><span class="line"><span class="code"> 1   hasher.combine(topLeft.y)</span></span><br><span class="line"><span class="code"> 1   hasher.combine(bottomRight.x)  (在 bottomRight.hash(into:) 里)</span></span><br><span class="line"><span class="code"> 1   hasher.combine(bottomRight.y)</span></span><br><span class="line"><span class="section"> 3   hasher.finalize()             (在 GridRectangle.hash(into:) 外面)</span></span><br><span class="line"><span class="section">---</span></span><br><span class="line"><span class="code"> 7</span></span><br></pre></td></tr></table></figure><!--Switching to `hash(into:)` gets us more robust hash values faster, andwith cleaner, simpler code.--><p>切换到 <code>hash(into:)</code> 之后让我们可以使用更加高效，健壮的方式去获取哈希值，同时代码也更加简洁。</p><h2 id="具体设计"><a href="#具体设计" class="headerlink" title="具体设计"></a><a name="detailed-design">具体设计</a></h2><h3 id="Hasher"><a href="#Hasher" class="headerlink" title="Hasher"></a><a name="hasher-details"><code>Hasher</code></a></h3><!--Add the following type to the standard library:--><p>给标准库添加下面的类型：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">/// 表示 `Set` 和 `Dictionary` 使用的通用哈希函数。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 这个哈希函数是一个 128-bit 的种子值和一个随机字节序列到一个整型哈希值的映射。</span></span><br><span class="line"><span class="comment">/// 种子值会在 `Hasher` 初始化时确定下来，而 `combine` 函数</span></span><br><span class="line"><span class="comment">/// 则会在填充字节序列时调用。当所有的字节都填充到 hasher 的时候，</span></span><br><span class="line"><span class="comment">/// 哈希值可以通过调用 `finalize()` 获取到：</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">///     var hasher = Hasher()</span></span><br><span class="line"><span class="comment">///     hasher.combine(23)</span></span><br><span class="line"><span class="comment">///     hasher.combine(&quot;Hello&quot;)</span></span><br><span class="line"><span class="comment">///     let hashValue = hasher.finalize()</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 内部的哈希算法是为了雪崩效应而设计的：种子值或者输入的字节序列微小的</span></span><br><span class="line"><span class="comment">/// 差异就会让最终产生的哈希值产生巨大的改变。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// - 注意：`Hasher` 的种子通常是随机生成的，这意味着每一次程序的运行都会产生不同的值。</span></span><br><span class="line"><span class="comment">///   `Hasher` 实现的哈希算法在不同版本的标准库里也会有差异，所以不要在运行期里</span></span><br><span class="line"><span class="comment">///   保存哈希值到下一次运行里复用。</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">struct</span> <span class="title class_">Hasher</span> &#123;</span><br><span class="line">  <span class="comment">/// 通过一个运行期产生的随机值初始化。</span></span><br><span class="line">  <span class="comment">/// 种子通常会通过一个高质量的随机来源获取，在程序启动时就确定下来。</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">init</span>()</span><br><span class="line">  </span><br><span class="line">  <span class="comment">/// 给 hasher 填充 `value`，把必要的部分糅合到 hasher 的状态里</span></span><br><span class="line">  <span class="keyword">@inlinable</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">combine</span>&lt;<span class="type">H</span>: <span class="type">Hashable</span>&gt;(<span class="keyword">_</span> <span class="params">value</span>: <span class="type">H</span>) &#123;</span><br><span class="line">    value.hash(into: <span class="operator">&amp;</span><span class="keyword">self</span>)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">/// 把 `buffer` 里的原始字节喂给 hasher，把它的 bits 揉合到 hasher 的状态里</span></span><br><span class="line">  <span class="keyword">public</span> <span class="keyword">mutating</span> <span class="keyword">func</span> <span class="title function_">combine</span>(<span class="params">bytes</span> <span class="params">buffer</span>: <span class="type">UnsafeRawBufferPointer</span>)</span><br><span class="line">  </span><br><span class="line">  <span class="comment">/// 结束 hasher 的状态并且返回一个哈希值</span></span><br><span class="line">  <span class="comment">///</span></span><br><span class="line">  <span class="comment">/// finalize 会将 hasher 的状态耗尽：手动 finalize 一个不属于你的 hasher，</span></span><br><span class="line">  <span class="comment">/// 或者对一个已经结束的 hasher 进行任何操作都是非法的。</span></span><br><span class="line">  <span class="comment">///（以后这些都可能会成为编译错误）</span></span><br><span class="line">  <span class="keyword">public</span> __consuming <span class="keyword">func</span> <span class="title function_">finalize</span>() -&gt; <span class="type">Int</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Hashable"><a href="#Hashable" class="headerlink" title="Hashable"></a><a name="hashable-details"><code>Hashable</code></a></h2><!--Change the `Hashable` protocol as follows.--><p>把 <code>Hashable</code> 协议改成下面这样：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">/// 一个可以被哈希到 `Hasher` 里的类型。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 你可以使用任意遵循 `Hashable` 协议的类型作为 Set 或者 Dictionary 的 key。</span></span><br><span class="line"><span class="comment">/// 许多标准库里的类型都遵循 `Hashable`：</span></span><br><span class="line"><span class="comment">/// 字符串，整型，浮点数和布尔类型，甚至 Set 默认也是 Hashable 的。一些其他类型，</span></span><br><span class="line"><span class="comment">/// 例如 Optional，Array 和 Range 在元素为 Hashable 时也会遵循 Hashable。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 你自定义的类型也可以是 Hashable 的。当你定义了一个不带关联值的枚举时，</span></span><br><span class="line"><span class="comment">/// 它可以自动实现 Hashable，并且你也可以通过实现 `hash(into:)` 函数去遵循 `Hashable`。</span></span><br><span class="line"><span class="comment">/// 对于那些所有成员变量都是 `Hashable` 的结构体，以及那些所有关联值都是 `Hashable` 的枚举类型，</span></span><br><span class="line"><span class="comment">/// 编译器都可以为它们自动生成 `hash(into:)` 的实现。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 哈希一个值意味着把它的必要部分填充到一个哈希函数里，这就是 `Hasher` 类型的抽象。</span></span><br><span class="line"><span class="comment">/// 这里的必要部分指的就是那些在 `Equatable` 实现里进行比较的部分。</span></span><br><span class="line"><span class="comment">/// 两个相等的实例必须在 `hash(into:)` 里以同样的顺序向 `Hasher` 填充同样的数据。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 遵循 Hashable 协议</span></span><br><span class="line"><span class="comment">/// ===================================</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 为了在 Set 里使用你的自定义类型或者在 Dictionary 里把它作为 Key 类型，</span></span><br><span class="line"><span class="comment">/// 请让它遵循 `Hashable` 协议。`Hashable` 协议是继承自 `Equatable` 协议的，</span></span><br><span class="line"><span class="comment">/// 所以你必须也让它遵循协议的实现。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 一个自定义类型的 `Hashable` 和 `Equatable` 实现可以由编译器为你自动生成，</span></span><br><span class="line"><span class="comment">/// 只要你的类型声明了 `Hashable` 的遵循，并且符合以下要求：</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// - 对于一个 `struct`，它的所有存储变量都必须遵循 `Hashable`。</span></span><br><span class="line"><span class="comment">/// - 对于一个 `enum`，它的所有关联值都必须遵循 `Hashable`。（一个没有关联值的枚举</span></span><br><span class="line"><span class="comment">///   甚至不需要显式声明 `Hashable` 的遵循）</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 为了自定义你的类型的 `Hashable` 遵循，让不符合以上条件的类型也可以遵循 `Hashable`，</span></span><br><span class="line"><span class="comment">/// 或者是拓展一个现有的类型让它遵循 `Hashable`，只要你手动实现 `hash(into:)` 方法</span></span><br><span class="line"><span class="comment">/// 即可，同时为了保证你的类型能够符合 `Hashable` 和 `Equatable` 协议的语义，</span></span><br><span class="line"><span class="comment">/// 最好也手动实现 Equatable。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 举一个例子，我们设想使用 `GridPoint` 类型描述一个网格的点击位置。</span></span><br><span class="line"><span class="comment">/// 这是 `GridPoint` 类型的初始声明：</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">///     /// 一个 x-y 坐标系统里的点</span></span><br><span class="line"><span class="comment">///     struct GridPoint &#123;</span></span><br><span class="line"><span class="comment">///         var x: Int</span></span><br><span class="line"><span class="comment">///         var y: Int</span></span><br><span class="line"><span class="comment">///     &#125;</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 你需要创建一个 grid point 的集合，去表示用户已经点击过的点。</span></span><br><span class="line"><span class="comment">/// 因为 `GridPoint` 类型不是 hashable 的，所以他不能作为 Set 里的 `Element`。</span></span><br><span class="line"><span class="comment">/// 为了遵循 `Hashable`，我们需要提供实现 `==` 函数和 `hash(into:)` 函数。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">///     extension GridPoint: Hashable &#123;</span></span><br><span class="line"><span class="comment">///         func hash(into hasher: inout Hasher) &#123;</span></span><br><span class="line"><span class="comment">///             hasher.combine(x)</span></span><br><span class="line"><span class="comment">///             hasher.combine(y)</span></span><br><span class="line"><span class="comment">///         &#125;</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">///         static func == (lhs: GridPoint, rhs: GridPoint) -&gt; Bool &#123;</span></span><br><span class="line"><span class="comment">///             return lhs.x == rhs.x &amp;&amp; lhs.y == rhs.y</span></span><br><span class="line"><span class="comment">///         &#125;</span></span><br><span class="line"><span class="comment">///     &#125;</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// `hash(into:)` 在这个例子里需要把属性 `x` 和 `y` 填充到 hasher 里，</span></span><br><span class="line"><span class="comment">/// 并且在 `==` 里对相同的元素进行比较。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">/// 现在 `GridPoint` 遵循了 `Hashable` 协议，你可以创建一个已经点击过的坐标点的集合。</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="comment">///     var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)]</span></span><br><span class="line"><span class="comment">///     let nextTap = GridPoint(x: 0, y: 1)</span></span><br><span class="line"><span class="comment">///     if tappedPoints.contains(nextTap) &#123;</span></span><br><span class="line"><span class="comment">///         print(&quot;监测到一个已经点击过的点 (\(nextTap.x), \(nextTap.y))。&quot;)</span></span><br><span class="line"><span class="comment">///     &#125; else &#123;</span></span><br><span class="line"><span class="comment">///         tappedPoints.insert(nextTap)</span></span><br><span class="line"><span class="comment">///         print(&quot;监测到一个新的点击点 (\(nextTap.x), \(nextTap.y))。&quot;)</span></span><br><span class="line"><span class="comment">///     &#125;</span></span><br><span class="line"><span class="comment">///     // 打印结果 &quot;监测到一个新的点击点 (0, 1).&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">protocol</span> <span class="title class_">Hashable</span>: <span class="title class_ inherited__">Equatable</span> &#123;</span><br><span class="line">  <span class="comment">/// 哈希值。</span></span><br><span class="line">  <span class="comment">///</span></span><br><span class="line">  <span class="comment">/// 哈希值只在同一次运行里保证相同，所以不要在运行期间保存哈希值并且到</span></span><br><span class="line">  <span class="comment">/// 下一次运行时使用。</span></span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">/// 通过调用 `hasher` 的 `combine` 函数，把这个值的必要部分填充到 `hasher` 里.</span></span><br><span class="line">  <span class="comment">///</span></span><br><span class="line">  <span class="comment">/// 准确的来说，必要的部分指的就是在 `Equatable` 的实现里拿来比较的那一部分。</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a><a name="source-compatibility">代码兼容性</a></h2><!--The introduction of the new `Hasher` type is a purely additive change.However, adding the `hash(into:)` requirement is potentially sourcebreaking. To ensure we keep compatibility with code written forprevious versions of Swift, while also allowing new code to onlyimplement `hash(into:)`, we extend [SE-0185]'s automatic `Hashable`synthesis to automatically derive either of these requirements whenthe other has been manually implemented.--><p>引入新的 <code>Hasher</code> 类型是一个单纯的增量修改。然而，添加 <code>hash(into:)</code> 实现要求会破坏代码稳定。为了保证我们兼容旧版的代码，在允许新代码只实现 <code>hash(into:)</code> 的同时，我们拓展了 <a href="0185-synthesize-equatable-hashable.md">SE-0185</a> 里的 <code>Hashable</code> 自动实现，以便让类型遵循老的 <code>Hashable</code> 时，可以自动为类型实现新的实现要求。</p><!--Code written for Swift 4.1 or earlier will continue to compile (in thecorresponding language mode) after this proposal is implemented. Thecompiler will synthesize the missing `hash(into:)` requirementautomatically:--><p>提案实现之后，使用 Swift 4.1 或更早版本编写的代码仍然可以通过编译（在正确的语言模式下）。编译器将会自动合成缺失的 <code>hash(into:)</code> 的实现要求：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">GridPoint41</span>: <span class="title class_ inherited__">Hashable</span> &#123; <span class="comment">// Swift 4.2 之前写的代码</span></span><br><span class="line">  <span class="keyword">let</span> x: <span class="type">Int</span></span><br><span class="line">  <span class="keyword">let</span> y: <span class="type">Int</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> x.hashValue <span class="operator">^</span> y.hashValue <span class="operator">&amp;*</span> <span class="number">16777619</span></span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 由编译器自动实现</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>) &#123;</span><br><span class="line">    hasher.combine(<span class="keyword">self</span>.hashValue)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--The compiler will emit a deprecation warning when it needs to do thisin 4.2 mode. (However, note that code that merely uses `hashValue`will continue to compile without warnings.)--><p>但使用 Swift 4.2 的模式时，编译器会自动抛出一个弃用的警告。（然而，注意，如果仅仅只是使用了 <code>hashValue</code> 的代码，依旧可以通过编译并且不会有编译警告）</p><!--Code written for Swift 4.2 or later should conform to `Hashable` byimplementing `hash(into:)`. The compiler will then complete theconformance with a suitable `hashValue` definition:--><p>使用 Swift 4.2 或者更新版本写出来的代码，必须满足 <code>Hashable</code> 的 <code>hash(into:)</code> 实现需求。编译器将会自动使用一个合适的 <code>hashValue</code> 实现。</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">GridPoint42</span>: <span class="title class_ inherited__">Hashable</span> &#123; <span class="comment">// 使用 Swift 4.2 编写的代码  </span></span><br><span class="line">  <span class="keyword">let</span> x: <span class="type">Int</span></span><br><span class="line">  <span class="keyword">let</span> y: <span class="type">Int</span></span><br><span class="line">  </span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>) &#123;</span><br><span class="line">    hasher.combine(x)</span><br><span class="line">    hasher.combine(y)</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 由编译器自动实现：</span></span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> hasher <span class="operator">=</span> <span class="type">Hasher</span>()</span><br><span class="line">    <span class="keyword">self</span>.hash(into: <span class="operator">&amp;</span>hasher)</span><br><span class="line">    <span class="keyword">return</span> hasher.finalize()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--When upgrading to Swift 4.2, `Hashable` types written for earlierversions of Swift should be migrated to implement `hash(into:)`instead of `hashValue`. --><p>在升级到 Swift 4.2 后，<code>Hashable</code> 类型在旧版里的实现都应该迁移到 <code>hash(into:)</code>。</p><!--For types that satisfy [SE-0185]'s requirements for `Hashable`synthesis, this can be as easy as removing the explicit `hashValue`implementation. Note that Swift 4.2 implements conditional `Hashable`conformances for many standard library types that didn't have itbefore; this enables automatic `Hashable` synthesis for types that usethese as components.--><p>而符合 <a href="0185-synthesize-equatable-hashable.md">SE-0185</a> 里的要求自动生成 <code>Hashable</code> 实现的，可以直接去除掉显式的 <code>hashValue</code> 实现。注意，Swift 4.2 通过 Conditional Conformance 给许多标准库的类型实现了 <code>Hashable</code>；这让 <code>Hashable</code> 的自动实现可以使用这些类型去作为组件。</p><!--For types that still need to manually implement `Hashable`, themigrator can be updated to help with this process. For example, the`GridPoint41.hashValue` implementation above can be mechanicallyrewritten as follows:--><p>而那些还需要手动实现 <code>Hashable</code> 的类型，迁移程序可以帮助你完成升级的过程。例如，上面 <code>GridPoint41.hashValue</code> 的实现应该像下面这样重写：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">GridPoint41</span>: <span class="title class_ inherited__">Hashable</span> &#123; </span><br><span class="line">  <span class="comment">// 从 Swift 4.1 迁移过来的代码</span></span><br><span class="line">  <span class="keyword">let</span> x: <span class="type">Int</span></span><br><span class="line">  <span class="keyword">let</span> y: <span class="type">Int</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">// 从 hashValue 迁移过来：</span></span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>) &#123;</span><br><span class="line">    hash.combine(x.hashValue <span class="operator">^</span> y.hashValue <span class="operator">&amp;*</span> <span class="number">16777619</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--This will not provide the same hash quality as combining both membersone by one, but it may be useful as a starting point.--><p>这么做无法像逐个元素 combine 那样产生高质量的哈希，但这可以作为一个不错的开始。</p><h2 id="对于-ABI-稳定的影响"><a href="#对于-ABI-稳定的影响" class="headerlink" title="对于 ABI 稳定的影响"></a><a name="abi">对于 ABI 稳定的影响</a></h2><!--`Hasher` and `hash(into:)` are additive changes that extend the ABI ofthe standard library. `Hasher` is a fully resilient struct, withopaque size/layout and mostly opaque members. (The only exception isthe generic function `combine(_:)`, which is provided as a syntacticconvenience.)--><p><code>Hasher</code> 和 <code>hash(into:)</code> 对于标准库的 ABI 页是单纯的增量修改。<code>Hasher</code> 是一个 resilient 的结构体，拥有不透明的内存布局以及几乎不透明的成员。（唯一一个例外就是泛型函数 <code>combine(_:)</code>，仅仅是作为语法糖的存在）</p><!--While this proposal deprecates the `hashValue` requirement, it doesn'tremove it. Types implementing `Hashable` will continue to provide animplementation for it, although the implementation may be providedautomatically by compiler synthesis.--><p>虽然这个计划会把 <code>hashValue</code> 标记为弃用，但不会移除它。实现了 <code>Hashable</code> 的类型可以继续保留这个实现，尽管具体实现可能是由编译器自动合成的。</p><!--To implement nondeterminism, `Hasher` uses an internal seed valueinitialized by the runtime during process startup. The seed is usuallyproduced by a random number generator, but this may be disabled bydefining the `SWIFT_DETERMINISTIC_HASHING` environment variable with avalue of `1` prior to starting a Swift process.--><p>为了实现不确定性，程序启动时会由 runtime 初始化一个种子值给 <code>Hasher</code> 使用。种子通常是随机生成的，但可以通过 <code>SWIFT_DETERMINISTIC_HASHING</code> 这个环境变量来关闭掉这个功能，设置为 <code>1</code> 即可。</p><h2 id="对于-API-稳定的影响"><a href="#对于-API-稳定的影响" class="headerlink" title="对于 API 稳定的影响"></a><a name="resilience">对于 API 稳定的影响</a></h2><!--Replacing `hashValue` with `hash(into:)` moves the responsibility ofchoosing a suitable hash function out of `Hashable` implementationsand into the standard library, behind a resiliency boundary.--><p>把 <code>hashValue</code> 替换成换成 <code>hash(into:)</code>，把哈希函数的选择从 <code>Hashable</code> 里剥离了出来，放到了标准库里，在 resilient 的边界之内。</p><!--`Hasher` is explicitly designed so that future versions of thestandard library will be able to replace the hash function.`Hashable` implementations compiled for previous versions willautomatically pick up the improved algorithm when linked with the newrelease. This includes changing the size or internal layout of the`Hasher` state itself.--><p><code>Hasher</code> 的设计让未来版本的标准库可以替换掉哈希函数。使用旧版本编译的 <code>Hashable</code> 在 link 到新版本的时候可以使用新的算法。这也包含了对 <code>Hasher</code> 内存布局的修改。</p><!--(We foresee several reasons why we may want to replace the hashfunction. For example, we may need to do so if a weakness isdiscovered in the current function, to restore the reliability of`Set` and `Dictionary`. We may also want to tweak the hash function toadapt it to the special requirements of certain environments (such asnetwork services), or to generally improve hashing performance.)--><p>（我们预想了几种替换掉哈希函数的情况。例如，我们也许需要在当前函数的漏洞被发现时这么做，去恢复 <code>Set</code> 和 <code>Dictionary</code> 的可靠性，我们也许还想要调整哈希函数去应对特殊的场景特殊的需要（例如网络服务），又或者只是哈希的性能优化）</p><h2 id="其它弃用的方案"><a href="#其它弃用的方案" class="headerlink" title="其它弃用的方案"></a><a name="alternatives">其它弃用的方案</a></h2><h3 id="保留-Hashable-原本的定义"><a href="#保留-Hashable-原本的定义" class="headerlink" title="保留 Hashable 原本的定义"></a><a name="leave-hashable-alone">保留 <code>Hashable</code> 原本的定义</a></h3><!--One option that we considered is to expose `Hasher`, but to leave the`Hashable` protocol as is. Individual `Hashable` types would be ableto choose whether or not to use it or to roll their own hashfunctions.--><p>我们考虑的其中一个方案是暴露 <code>Hasher</code>，但保留 <code>Hashable</code> 原本的实现。独立的 <code>Hashable</code> 类型可以选择是否使用 <code>Hasher</code>，或者采取它们自己的哈希函数。</p><!--We felt this was an unsatisfying approach; the rationale behind thisis explained in the section on [The `hash(into:)` requirement](#hash-into).--><p>我们觉得这个做法并不那么令人满意；根本原因在 <a href="#hash-into"><code>hash(into:)</code> 实现要求</a> 里有比较详细的说明。</p><h3 id="定义一个新的协议"><a href="#定义一个新的协议" class="headerlink" title="定义一个新的协议"></a><a name="new-protocol">定义一个新的协议</a></h3><!--There have been several attempts to fix `Hashable` by creating a newprotocol to replace it. For example, there's a prototypeimplementation of a [`NewHashable` protocol][h1] in the Swift testsuite. The authors of this proposal have done their share of this,too: Karoy has previously published an open-source [hashingpackage providing an opt-in replacement for `Hashable`][h2], whileVincent wrote [a detailed pitch for adding a `HashVisitable` protocolto the standard library][h3a] -- these efforts were direct precursorsto this proposal.--><p>有过几次创建新协议取代 <code>Hashable</code> 的尝试。例如，在 Swift 的测试里有一个叫做 <a href="https://github.com/apple/swift/blob/swift-4.1-branch/validation-test/stdlib/HashingPrototype.swift"><code>NewHashable</code> protocol</a> 的原型实现。提案的作者们也有过自己的尝试并且分享了出来：Karoy 早前开源了<a href="https://github.com/attaswift/SipHash">哈希工具包，提供了内置的 <code>Hashable</code> 替代方案</a>，并且 Vincent 也写了一篇 <a href="https://blog.definiteloops.com/ha-r-sh-visitors-8c0c3686a46f">添加 <code>HashVisitable</code> 协议到标准库的提案</a> —— 这些都是这个提案的先驱者。</p><!--In these approaches, the new protocol could either be a refinement of`Hashable`, or it could be unrelated to it. Here is what a refinementwould look like:--><p>在这些方案里，都是定义一个新的协议去替代 <code>Hashable</code>，或者是与 <code>Hashable</code> 无关的一些方案。这是其中一部分：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Hashable</span>: <span class="title class_ inherited__">Equatable</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123; <span class="keyword">get</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">protocol</span> <span class="title class_">Hashable2</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Hashable2</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> hashValue: <span class="type">Int</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> hasher <span class="operator">=</span> <span class="type">Hasher</span>()</span><br><span class="line">    hash(into: <span class="operator">&amp;</span>hasher)</span><br><span class="line">    <span class="keyword">return</span> hasher.finalize()</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--While this is a great approach for external hashing packages, webelieve it to be unsuitable for the standard library. Adding a newprotocol would add a significant new source of user confusion abouthashing, and it would needlessly expand the standard library's APIsurface area.--><p>虽然对于一个外部的哈希库来说是一个很合适的做法，但我们认为它并不适用于标准库。添加一个新的协议会显著增加新用户关于哈希的认知负担，并且也会让标准库增加一些不必要的 API。</p><!--The new protocol would need to have a new name, but `Hashable` alreadyhas the perfect name for a protocol representing hashable things -- sowe'd need to choose an imperfect name for the "better" protocol.--><p>新的协议需要有一个新的名字，但 <code>Hashable</code> 已经是哈希相关的协议里最好的名字了 —— 所以我们需要为这个“更好”的协议选一个没那么好的名字。</p><!--While deprecating a protocol requirement is a significant change, webelieve it to be less harmful overall than leaving `Hashable`unchanged, or trying to have two parallel protocols for the same thing.--><p>相比起完整保留 <code>Hashable</code> 或者是同时使用两个协议，我们认为弃用一个协议的实现要求虽然会产生很大的变动，但总体来说是值得的。</p><!--Additionally, adding a second protocol would lead to complicationswith `Hashable` synthesis. It's also unclear how `Set` and `Dictionary`would be able to consistently use `Hasher` for their primary hashingAPI. (These problems are not unsolvable, but they may involve addingspecial one-off compiler support for the new protocol. For example, wemay want to automatically derive `Hashable2` conformance for all typesthat implement `Hashable`.)--><p>另外，添加新协议会让 <code>Hashable</code> 整体变得更复杂。同时 <code>Set</code> 和 <code>Dictionary</code> 应该如何使用 <code>Hasher</code>（这些问题都没有很好的得到解决，但它们也许还需要一些一次性的编译支持，例如，我们也许会想要让所有遵循了 <code>Hashable2</code> 的类型自动实现 <code>Hashable</code>）</p><h3 id="让-Hasher-成为一个协议，使得-hash-into-变成一个泛型函数"><a href="#让-Hasher-成为一个协议，使得-hash-into-变成一个泛型函数" class="headerlink" title="让 Hasher 成为一个协议，使得  hash(into:) 变成一个泛型函数"></a><a name="generic-hasher">让 <code>Hasher</code> 成为一个协议，使得  <code>hash(into:)</code> 变成一个泛型函数</a></h3><!--It would be nice to allow Swift programmers to define their own hashfunctions, and to plug them into any `Hashable` type:--><p>让 Swift 程序员可以定义自己的哈希函数，并且把它们应用到任意的 <code>Hashable</code> 类型是一件很棒的事情：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Hasher</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">combine</span>(<span class="params">bytes</span>: <span class="type">UnsafeRawBufferPointer</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">protocol</span> <span class="title class_">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>&lt;<span class="type">H</span>: <span class="type">Hasher</span>&gt;(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">H</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--However, we believe this would add a degree of generality whose costsare unjustifiable relative to their potential gain. We expect theability to create custom hashers would rarely be exercised. Forexample, we do not foresee a need for adding support for customhashers in `Set` and `Dictionary`. On the other hand, there aredistinct advantages to standardizing on a single, high-quality hashfunction:--><p>然而，这会增加一个新的讨论维度，并且很难评判其收益。我们认为自定义 hasher 是一个很小众的需求。例如，我们想不到将来给 <code>Set</code> 和 <code>Dictionary</code> 增加一个自定义 hasher 的需要。而另一方面，标准化，使用单一的，高质量的哈希函数的优势反而很明显：</p><!--* Adding a generic type parameter to `hash(into:)` would complicate    the `Hashable` API.* By supporting only a single `Hasher`, we can concentrate our efforts    on making sure it runs fast. For example, we know that the    standard hasher's opaque mutating functions won't ever perform any    retain/release operations, or otherwise mutate any of the    reference types we may encounter during hashing; describing this    fact to the compiler enables optimizations that would not    otherwise be possible.* Generics in Swift aren't zero-cost abstractions. We may be tempted    to think that we could gain some performance by plugging in a less    sophisticated hash function. This is not necessarily the case --    support for custom hashers comes with significant overhead that    can easily overshadow the (slight, if any) algorithmic    disadvantage of the standard `Hasher`.-->    <ul><li>给 <code>hash(into:)</code> 增加一个泛型参数会让 <code>Hashable</code> 的 API 变得复杂。</li><li>只支持一种 <code>Hasher</code> 的话，我们可以投入所有精力去让它运行得足够快。例如，我们知道标准的 hasher 内部的 mutating 函数，或者在哈希值计算期间任何引用类型的 mutation 都不会触发任何 retain&#x2F;release 操作；让编译器了解这些信息可以得到更多的优化，否则很难做到。</li><li>Swift 里的泛型不是 zero-cost 的抽象。我们也许会倾向于认为通过使用一些为场景特殊优化过的哈希函数可以获取到更高的性能表现，但这并不是必然的 —— 支持自定义的 hasher 的性能消耗反而会掩盖掉算法带来的提升。</li></ul><!--Note that the proposed non-generic `Hasher` still has full support forBloom filters and other data structures that require multiple hashfunctions. (To select a different hash function, we just need tosupply a new seed value.)--><p>注意，非泛型的 <code>Hasher</code> 是完全支持 Bloom filter 或者其他需要多个哈希函数的数据结构。（选择另外的哈希函数时，只要支持一个新的种子值就可以了）</p><h3 id="把-hash-into-的参数改为闭包，而不是一个新的类型"><a href="#把-hash-into-的参数改为闭包，而不是一个新的类型" class="headerlink" title="把 hash(into:) 的参数改为闭包，而不是一个新的类型"></a><a name="closure-hasher">把 <code>hash(into:)</code> 的参数改为闭包，而不是一个新的类型</a></h3><!--A variant of the previous idea is to represent the hasher by a simpleclosure taking an integer argument:--><p>上一个方案的另一个变种就是，把 hasher 声明为一个简单的闭包参数：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: (<span class="type">Int</span>) -&gt; <span class="type">Void</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">GridPoint</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">  <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: (<span class="type">Int</span>) -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    hasher(x)</span><br><span class="line">    hasher(y)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><!--While this is an attractively minimal API, it has problems withgranularity -- it doesn't allow adding anything less than an`Int`'s worth of bits to the hash state.--><p>虽然这是一个简单有效的 API，但它存在颗粒度的问题 —— 它不支持任何除了 <code>Int</code> 以外的有效字节加入到哈希的状态里。</p><!--Additionally, like generics, the performance of such a closure-basedinterface would compare unfavorably to `Hasher`, since the compilerwouldn't be able to guarantee anything about the potentialside-effects of the closure.--><p>另外，就像泛型，这种基于闭包的接口性能相比起 <code>Hasher</code> 来说不够好，因为编译器无法保证闭包不产生任何潜在的副作用。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2018-10-12/</id>
    <link href="https://kemchenj.github.io/2018-10-12/"/>
    <published>2018-10-11T16:00:00.000Z</published>
    <summary>
      <![CDATA[<ul>
<li>提案: <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0206-hashable-enhancements.md">SE-0206</a></li>
<li>作者: <a href="https://github.com/lorentey">Karoy Lorentey</a>, <a href="https://github.com/regexident">Vincent Esche</a></li>
<li>审核人: <a href="https://github.com/jckarter">Joe Groff</a></li>
<li>状态: <strong>已实现 (Swift 4.2)</strong></li>
<li>决策记录: <a href="https://forums.swift.org/t/accepted-se-0206-hashable-enhancements/11675/115">Rationale</a></li>
<li>实现：<ul>
<li><a href="https://github.com/apple/swift/pull/14913">apple&#x2F;swift#14913</a> (standard library, underscored),</li>
<li><a href="https://github.com/apple/swift/pull/16009">apple&#x2F;swift#16009</a> (<code>Hasher</code> interface),</li>
<li><a href="https://github.com/apple/swift/pull/16073">apple&#x2F;swift#16073</a> (automatic synthesis, de-underscoring)</li>
</ul>
</li>
<li>上一个版本: <a href="https://github.com/apple/swift-evolution/blob/f5a020ec79cdb64fc8700af91b1a1ece2d2fb141/proposals/0206-hashable-enhancements.md">1</a></li>
</ul>
<!--
*During the review process, add the following fields as needed:*

* Decision Notes: [Rationale](https://forums.swift.org/), [Additional Commentary](https://forums.swift.org/)
* Bugs: [SR-NNNN](https://bugs.swift.org/browse/SR-NNNN), [SR-MMMM](https://bugs.swift.org/browse/SR-MMMM)
* Previous Proposal: [SE-XXXX](XXXX-filename.md)
-->

<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><ul>
<li><a href="#intro">简介</a></li>
<li><a href="#why">提案缘由</a><ul>
<li><a href="#status-quo">现状</a></li>
<li><a href="#universal-hashing">通用的哈希函数</a></li>
</ul>
</li>
<li><a href="#proposed-solution">解决方案</a><ul>
<li><a href="#hasher"><code>Hasher</code> 结构体</a></li>
<li><a href="#hash-into"><code>hash(into:)</code> 实现要求</a></li>
</ul>
</li>
<li><a href="#detailed-design">细节设计</a><ul>
<li><a href="#hasher-details"><code>Hasher</code></a></li>
<li><a href="#hashable-details"><code>Hashable</code></a></li>
</ul>
</li>
<li><a href="#source-compatibility">代码兼容性</a></li>
<li><a href="#abi">对于 ABI 稳定的影响</a></li>
<li><a href="#resilience">对于 API 稳定的影响</a></li>
<li><a href="#alternatives">其它弃用的方案</a><ul>
<li><a href="#leave-hashable-alone">保留 <code>Hashable</code> 原本的定义</a></li>
<li><a href="#new-protocol">定义一个新协议</a></li>
<li><a href="#generic-hasher">让 <code>Hasher</code> 成为一个协议，使得  <code>hash(into:)</code> 变成一个泛型函数</a></li>
<li><a href="#closure-hasher">把 <code>hash(into:)</code> 的参数改为闭包，而不是一个新的类型</a></li>
</ul>
</li>
</ul>]]>
    </summary>
    <title>【译】SE-0206 Hashable 加强</title>
    <updated>2026-09-02T11:08:19.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>以往我们实现 <code>debug</code> 函数，一般都是直接使用 compile flag （也就是编译宏）去实现的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">debug</span>(<span class="keyword">_</span> <span class="params">action</span>: () -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    <span class="keyword">#if</span> <span class="type">DEBUG</span></span><br><span class="line">    action()</span><br><span class="line">    <span class="keyword">#endif</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但这种方式需要修改工程文件的设置，而且离开了 Xcode 就不能很好地使用。</p><p>今天看<a href="https://github.com/vapor/routing/blob/master/Sources/Routing/Utilities/RoutingError.swift"> Vapor 的源码</a>时看到了一个很巧妙的实现：</p><span id="more"></span><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">debugOnly</span>(<span class="keyword">_</span> <span class="params">body</span>: () -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    <span class="built_in">assert</span>(&#123; body(); <span class="keyword">return</span> <span class="literal">true</span> &#125;())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这是 <code>assert</code> 函数的声明：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">assert</span>(</span><br><span class="line">    <span class="keyword">_</span> <span class="params">condition</span>: <span class="keyword">@autoclosure</span> () -&gt; <span class="type">Bool</span>,</span><br><span class="line">    <span class="keyword">_</span> <span class="params">message</span>: <span class="keyword">@autoclosure</span> () -&gt; <span class="type">String</span> <span class="operator">=</span> <span class="keyword">default</span>,</span><br><span class="line">    <span class="params">file</span>: <span class="type">StaticString</span> <span class="operator">=</span> <span class="keyword">#file</span>,</span><br><span class="line">    <span class="params">line</span>: <span class="type">UInt</span> <span class="operator">=</span> <span class="keyword">#line</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p><code>condition</code> 由于 <code>@autoclosure</code> 的标记会把传入的值自动装到闭包里，然后只有在 debug 模式下才会执行并且求值，通过这种方式就可以很完美地实现一个 <code>debugOnly</code> 函数。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2018-09-24/</id>
    <link href="https://kemchenj.github.io/2018-09-24/"/>
    <published>2018-09-23T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>以往我们实现 <code>debug</code> 函数，一般都是直接使用 compile flag （也就是编译宏）去实现的：</p>
<figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">debug</span>(<span class="keyword">_</span> <span class="params">action</span>: () -&gt; <span class="type">Void</span>) &#123;</span><br><span class="line">    <span class="keyword">#if</span> <span class="type">DEBUG</span></span><br><span class="line">    action()</span><br><span class="line">    <span class="keyword">#endif</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>但这种方式需要修改工程文件的设置，而且离开了 Xcode 就不能很好地使用。</p>
<p>今天看<a href="https://github.com/vapor/routing/blob/master/Sources/Routing/Utilities/RoutingError.swift"> Vapor 的源码</a>时看到了一个很巧妙的实现：</p>]]>
    </summary>
    <title>巧妙实现 debugOnly 函数</title>
    <updated>2026-09-02T11:08:19.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>这个 Session 分为两个部分，前半部分会简单介绍一下 Swift 开源相关的事情，后半部分我们深入了解一下 Swift 4.2 带来了哪些更新。</p><h2 id="社区的发展"><a href="#社区的发展" class="headerlink" title="社区的发展"></a>社区的发展</h2><p>首先我们来看一下 Swift 的一些统计数据，Swift 自开源之后，总共有 600 个代码贡献者，合并了超过 18k pull request。</p><p><img src="/images/Screen%20Shot%202018-06-10%20at%2012.41.12.webp" alt="Screen Shot 2018-06-10 at 12.41.12"></p><span id="more"></span><h3 id="社区主导的持续集成"><a href="#社区主导的持续集成" class="headerlink" title="社区主导的持续集成"></a>社区主导的持续集成</h3><p>Swift 想要成为一门跨平台的泛用语言，大概一个月之前，Swift 团队拓展了原有的公开集成平台，叫做 Community-Hosted Continuous Integration，如果大家想要把 Swift 带到其它平台上，就可以在这上面去接入你们自己的硬件机器，Swift 会定期在这些硬件上跑一遍集成测试，这样大家就可以非常及时地了解到最新的 Swift 是否能在你们的平台上正常运行。</p><p><img src="/images/Screen%20Shot%202018-06-10%20at%2012.40.37.webp" alt="Screen Shot 2018-06-10 at 12.40.37"></p><h3 id="Swift-论坛"><a href="#Swift-论坛" class="headerlink" title="Swift 论坛"></a>Swift 论坛</h3><p>同时，Swift 的团队付出了很大的精力在维护 Swift 的社区上，两个月前 Swift 社区正式从邮件列表转向论坛，让大家可以更容易贡献自己的力量，例如说三月份的这一份提案：</p><p><img src="/images/Screen%20Shot%202018-06-10%20at%2013.13.30.webp" alt="Screen Shot 2018-06-10 at 13.13.30"></p><p>大家只要简单回答这些问题，参与讨论即可，如果你对于这方面的理解不深，不想贸然发言的话，其实只要大概阅读过社区成员们的发言，对这件事情有了解，那也是一种参与，以后也许这个提案出来了你还可以写篇文章跟大家讲讲当时讨论的内容和要点。</p><p>如果你在维护一个 Swift 相关的计划，你可以考虑在论坛上申请一个板块，让社区的人也可以关注到你的计划并且参与到其中来。</p><p><img src="/images/Screen%20Shot%202018-06-10%20at%2012.40.56.webp" alt="Screen Shot 2018-06-10 at 12.40.56"></p><p>Swift 的文档现在改为由 swift.org 来维护，网址是 <a href="docs.swift.org">docs.swift.org</a>。</p><h3 id="Chris-Lattner"><a href="#Chris-Lattner" class="headerlink" title="Chris Lattner"></a>Chris Lattner</h3><p>Chris Lattner 大神离开苹果的时候，有很多人在讨论 Swift 是不是已经没戏了，但过去一年，实际上 Chris 为 Swift 做了很多，去谷歌甚至可以说是去那里做 Swift 布道师。</p><p>Chris 进了谷歌之后，谷歌 fork 了一个 Swift 的仓库，作为谷歌里开发者 Commit 的中转站，过去一年修复了很多 Swift 在 Linux 上的运行问题，让 Swift 在 Linux 上的运行更加稳定。谷歌还写了一个 <a href="https://github.com/google/swift/tree/format/tools/swift-format/">Swift Formatter</a>，现在正在开发阶段。</p><p>并且与 Tensorflow 紧密合作，开发了 Swift for Tensorflow，主要是因为 python 已经渐渐无法满足 Tensorflow 的使用，上百万次的学习循环让性能表现变得异常重要，需要一门语言去跟 Tensorflow 有更紧密的交互，大家可能觉得其它语言也都可以使用 Tensorflow，没有什么特别，实际上其它语言都只是开发了 Tensorflow 的 API，而 Swift 代码则会被直接编译成 Tensorflow Graph，具有更强的性能，甚至 Tensorflow 团队还为 Swift 开发了专门的语法，让 Swift 变成 Tensorflow 里的一等公民。加入了与 Python 的交互之后，让 Swift 在机器学习领域得到了更加好的生态。</p><p>Chris 在过去一年，拉谷歌入局一起维护 Swift，加强 Swift 在 Linux 上的表现，还给 Swift 开辟了一个机器学习的领域，并且在 Swift 社区<a href="https://forums.swift.org/u/Chris_Lattner3">持续活跃贡献着自己的才华</a>，现在我想大家完全可以不必担心说 Chris 的离开会对 Swift 产生什么不好的影响。</p><p><img src="/images/Screen%20Shot%202018-06-10%20at%2013.03.37.webp" alt="Screen Shot 2018-06-10 at 13.03.37"></p><h2 id="What-is-Swift-4-2"><a href="#What-is-Swift-4-2" class="headerlink" title="What is Swift 4.2?"></a>What is Swift 4.2?</h2><p>接下来我们要了解一下 Swift 4.2，那么 Swift 4.2 是什么呢？它在整个开发周期中是一个什么样的角色？</p><p><img src="/images/Screen%20Shot%202018-06-07%20at%2007.18.41.webp" alt="Screen Shot 2018-06-07 at 07.18.41"></p><p>Swift 每半年就会有一次 Major Release，Swift 4.2 就是继 4.0 和 4.1 之后的一次 Major Release，官方团队一直致力于提升开发体验：</p><ul><li>更快的编译速度</li><li>增加功能提升代码编写效率</li><li>SDK 对于 Swift 更好的支持</li><li>提升 ABI 的兼容性</li></ul><p><img src="/images/Screen%20Shot%202018-06-07%20at%2007.21.00.webp" alt="Screen Shot 2018-06-07 at 07.21.00"></p><p>Swift 5 会在 2019 年前期正式发布，ABI 最终会在这一个版本里稳定下来，并且 Swift 的运行时也会内嵌到操作系统里，到时候 App 的启动速度会有进一步的提升，并且打包出来的程序也会变得更小。</p><p>如果大家对于 ABI 稳定的计划感兴趣的话，可以关注一下这一份进度表 <a href="https://swift.org/abi-stability/">ABI Dashboard</a>。</p><h3 id="编译器的改进"><a href="#编译器的改进" class="headerlink" title="编译器的改进"></a>编译器的改进</h3><h4 id="代码兼容性"><a href="#代码兼容性" class="headerlink" title="代码兼容性"></a>代码兼容性</h4><p>跟 Xcode 9 一样，Xcode 10 里也只会搭载一个 Swift 编译器，并且提供两种兼容模式，同时兼容之前的两个 Swift<br>版本，这三种模式都可以使用新的 API，新的语言功能。</p><p><img src="/images/Screen%20Shot%202018-06-09%20at%2010.28.40.webp" alt="Screen Shot 2018-06-09 at 10.28.40"></p><p>并且不只是 Swift 的语法层面的兼容，开发组三种模式也同时覆盖 SDK 的兼容，也就是说只要你的代码在 Xcode 8，Swift 3 的环境下能跑，那么在 Xcode 10 里使用兼容模式也肯定可以跑起来。</p><p>但 Swift 4.2 确实提供了更多优秀的功能，为了接下来的开发，这会是最后一个支持 Swift 3 兼容模式的版本。 </p><h4 id="更快的-Debug-编译速度"><a href="#更快的-Debug-编译速度" class="headerlink" title="更快的 Debug 编译速度"></a>更快的 Debug 编译速度</h4><p>接下来我们来讨论一下编译速度的提升，这是在 Macbook Pro 四核 i7 上测试现有 App 的结果：</p><p><img src="/images/Screen%20Shot%202018-06-09%20at%2010.40.22.webp" alt="Screen Shot 2018-06-09 at 10.40.22"></p><p>Wikipedia 是一个 Objective-C 和 Swift 混编的项目，可能更加贴近大家的实际项目，项目的编译速度实际上取决于很多方面，例如说项目的配置，图片文件的数量跟大小。</p><p>1.6 倍的提升是整体的速度，如果我们只关注 Swift 的编译时间的话，实际上它总共提升了 3 倍，对于很大一部分项目来说，一次全量编译大概可以比以前快两倍。</p><p>这些提升来自于哪里呢？由于 Swift 里并不需要导入头文件，但每一个文件由可以访问到模块里的其他文件里的内容，所以编译阶段会有大量的重复工作去进行 symbol 查找，这次编译器构建了一个编译 pipeline 去减少重复的跨文件执行。</p><h5 id="Compilation-Mode-vs-Optimization-Level"><a href="#Compilation-Mode-vs-Optimization-Level" class="headerlink" title="Compilation Mode vs. Optimization Level"></a>Compilation Mode vs. Optimization Level</h5><p><img src="/images/Screen%20Shot%202018-06-07%20at%2007.45.59.webp" alt="Screen Shot 2018-06-07 at 07.45.59"></p><p>另外这一次，把“编译模式”从“优化级别”里剥离了出来，编译模式意味着我们如何编译我们的模块，目前总共有两种模式：</p><ul><li><strong>增量化编译（Incremental）</strong>：也就是以前的 Single File，逐个文件编译。</li><li><strong>模块化编译（Whole Module）</strong>：整个模块一起编译。</li></ul><p>增量编译虽然全量编译一次会比模块化编译慢，但是之后修改一次文件就只需要再编译一次相关的文件即可，而不必整个模块都重新编译一次。</p><p>整个模块一起编译的话会更加快，据说原理是把所有文件都合并为一个文件，然后再进行编译，以此减少跨文件的 symbol 查找。但一旦改动了其中一个文件，就需要重新再把整个模块编译一遍。</p><p>增加了这个编译选项实际上还有一个很重要的意义，以前我们只有三种选项，可以达到下面三种效果：</p><table><thead><tr><th align="center"></th><th align="center">增量化编译</th><th align="center">模块化编译</th></tr></thead><tbody><tr><td align="center">优化</td><td align="center">✅</td><td align="center">✅</td></tr><tr><td align="center">不优化</td><td align="center">✅</td><td align="center">❌</td></tr></tbody></table><p>优化是需要消耗时间的的，现在我们可以使用<strong>模块化</strong>并且<strong>不优化</strong>的选项，达到最快的编译速度，把这个选项应用到我们项目里不经常改动的那一部分代码里的话（例如 pod 的依赖库），就可以大大提高我们的编译速度。</p><p>我这个配置应用到项目里之后，实测编译速度从 113s 加快到了到了 64s，只要在 podfile 里加入这一段代码就可以了（在 Xcode 9.3 也可以正常使用）：</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">post_install <span class="keyword">do</span> |<span class="params">installer</span>|</span><br><span class="line">  <span class="comment"># 提高 pod 库编译速度</span></span><br><span class="line">  installer.pods_project.targets.each <span class="keyword">do</span> |<span class="params">target</span>|</span><br><span class="line">    target.build_configurations.each <span class="keyword">do</span> |<span class="params">config</span>|</span><br><span class="line">      config.build_settings[<span class="string">&#x27;SWIFT_COMPILATION_MODE&#x27;</span>] = <span class="string">&#x27;wholemodule&#x27;</span></span><br><span class="line">      <span class="keyword">if</span> config.name == <span class="string">&#x27;Debug&#x27;</span></span><br><span class="line">        config.build_settings[<span class="string">&#x27;SWIFT_OPTIMIZATION_LEVEL&#x27;</span>] = <span class="string">&#x27;-Onone&#x27;</span></span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        config.build_settings[<span class="string">&#x27;SWIFT_OPTIMIZATION_LEVEL&#x27;</span>] = <span class="string">&#x27;-Osize&#x27;</span></span><br><span class="line">      <span class="keyword">end</span></span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h4 id="Runtime-优化"><a href="#Runtime-优化" class="headerlink" title="Runtime 优化"></a>Runtime 优化</h4><h5 id="ARC"><a href="#ARC" class="headerlink" title="ARC"></a>ARC</h5><p>Swift 使用 ARC 进行内存管理，ARC 是在 MRC 的基础上演进出来的，ARC 使用某种对象管理模型在编译时，在合适的位置自动为我们插入 retain 跟 release 代码。</p><p>Swift 4.2 之前使用的模型是“<strong>持有(owned)</strong>”模型，<strong>调用方</strong>负责 retain，<strong>被调用方</strong>负责 release，换句话就是说<strong>被调用方</strong>持有了传进来的对象，如下图所示：</p><p><img src="/images/Screen%20Shot%202018-06-09%20at%2017.13.51.webp" alt="Screen Shot 2018-06-09 at 17.13.51"></p><p>但实际上这种模型会产生很多不必要的 retain 跟 release，现在 Swift 4.2 改为使用**“担保(Guaranteed)”<strong>模型，由</strong>调用方**去保证对象在函数调用的生命周期内不会被 release 掉，<strong>被调用方</strong>不再持有对象：</p><p><img src="/images/Screen%20Shot%202018-06-09%20at%2017.20.22.webp" alt="Screen Shot 2018-06-09 at 17.20.22"></p><p>采取了这种模型之后，不止可以有更好的性能表现，还会让编译出来的二进制文件变得更小。</p><h5 id="String"><a href="#String" class="headerlink" title="String"></a>String</h5><p><img src="/images/Screen%20Shot%202018-06-09%20at%2017.24.14.webp" alt="Screen Shot 2018-06-09 at 17.24.14"></p><p>当我们在 64bit 的平台上实例化一个 String 的时候，它的长度是 16 bytes，为了存储不等长的内容，它会在堆里申请一段空间去存储，而那 16 个 bytes 里会存储着一些相关信息，例如编码格式，这是权衡了性能和内存占用之后的出来的结果。</p><p>但 16 bytes 的内存占用实际上还存在着优化空间，对于一些足够小的字符串，我们完全可以不必在堆里独立存储，而是放到这 16 个 bytes 里空余的部分，这样就可以让小字符串有更好的性能和更少的内存占用。</p><p>具体原理跟 NSString 的 <a href="https://mikeash.com/pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html">Tagged Pointer</a> 一样，但能比 NSString 存放稍微更大一点的字符串。</p><h4 id="减小代码尺寸"><a href="#减小代码尺寸" class="headerlink" title="减小代码尺寸"></a>减小代码尺寸</h4><p><img src="/images/Screen%20Shot%202018-06-07%20at%2007.58.24.webp" alt="Screen Shot 2018-06-07 at 07.58.24"></p><p>Swift 还增加了一个优化等级选项 “Optimize for Size”，名如其意就是优化尺寸，编译器通过减少泛型特例化，减少函数内联等等手段，让最终编译出来的二进制文件变得更小</p><p>现实中性能可能并非人们最关心的，而应用的大小会更加重要，使用了这个编译选项实测可以让二进制文件减小 10-30%，而性能通常会多消耗 5%。</p><h3 id="新的语法功能"><a href="#新的语法功能" class="headerlink" title="新的语法功能"></a>新的语法功能</h3><h4 id="可遍历枚举"><a href="#可遍历枚举" class="headerlink" title="可遍历枚举"></a>可遍历枚举</h4><p>以前我们为了遍历枚举值，可能会自己去实现一个 <code>allCases</code> 的属性：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">LogLevel</span> &#123;</span><br><span class="line">    <span class="keyword">case</span> warn</span><br><span class="line">    <span class="keyword">case</span> info</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">let</span> allCases: [<span class="type">LogLevel</span>] <span class="operator">=</span> [.warn, .info]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但我们在添加新的 case 的时候可能会忘了去更新 <code>allCases</code>，现在我们在 Swift 4.2 里可以使用 <code>CaseIterable</code> 协议，让编译器自动为我们创建 <code>allCases</code>：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">LogLevel</span>: <span class="title class_ inherited__">CaseIterable</span> &#123;</span><br><span class="line">    <span class="keyword">case</span> warn</span><br><span class="line">    <span class="keyword">case</span> info</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> level <span class="keyword">in</span> <span class="type">LogLevel</span>.allCases &#123;</span><br><span class="line">    <span class="built_in">print</span>(level)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Conditional-Conformance"><a href="#Conditional-Conformance" class="headerlink" title="Conditional Conformance"></a>Conditional Conformance</h4><p>Conditional Conformance 表达了这样的一个语义：泛型类型在特定条件下会遵循一个特定的协议。例如，Array 只会在它的元素为 Equatable 的时候遵循 Equatable：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Array</span>: <span class="title class_ inherited__">Equatable</span> <span class="keyword">where</span> <span class="title class_ inherited__">Element</span>: <span class="title class_ inherited__">Equatable</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">==&lt;</span><span class="type">T</span> : <span class="type">Equatable</span><span class="operator">&gt;</span>(lhs: <span class="type">Array</span>&lt;<span class="type">Element</span>&gt;, rhs: <span class="type">Array</span>&lt;<span class="type">Element</span>&gt;) -&gt; <span class="type">Bool</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这是一个非常强劲的功能，Swift 标准库里大量使用这个功能，Codable 也是通过这个功能去进行检查，帮助我们自动生成解析代码的.</p><h4 id="Hashable-的加强"><a href="#Hashable-的加强" class="headerlink" title="Hashable 的加强"></a>Hashable 的加强</h4><p>与 Codable 类似，Swift 4.2 为 <code>Equatable</code> 和 <code>Hashable</code> 引入了自动实现的功能：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Stock</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> market: <span class="type">String</span></span><br><span class="line">    <span class="keyword">var</span> code: <span class="type">String</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但这会带来一个问题，<code>hashValue</code> 该怎么实现？现有 <code>hashValue</code> 的 API 虽然简单，但却难以实现，你必须想出一种方法去把所有属性糅合起来然后产生一个哈希值，并且像 <code>Set</code> 和 <code>Dictionary</code> 这种围绕哈希表构建起来的序列，性能完全依赖于存储的元素的哈希实现，这是不合理的。</p><p>在 Swift 4.2 里，改进了 <code>Hashable</code> 的 API，引入了一个新的 <code>Hasher</code> 类型来存储哈希算法，新的 <code>Hashable</code> 长这个样子： </p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">protocol</span> <span class="title class_">Hashable</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>现在我们不需要在实现 Hashable 的时候就决定好具体的哈希算法，而是决定哪些属性去参与哈希的过程：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">extension</span> <span class="title class_">Stock</span>: <span class="title class_ inherited__">Hashable</span> &#123;</span><br><span class="line">    <span class="keyword">func</span> <span class="title function_">hash</span>(<span class="params">into</span> <span class="params">hasher</span>: <span class="keyword">inout</span> <span class="type">Hasher</span>) &#123;</span><br><span class="line">        market.hash(into: <span class="operator">&amp;</span>hasher)</span><br><span class="line">        code.hash(into: <span class="operator">&amp;</span>hasher)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样 <code>Dictionary</code> 就不再依赖于存储元素的哈希实现，可以自己选择一个高效的哈希算法去构建 <code>Hasher</code>，然后调用 <code>hash(into:)</code> 方法去获得一个哈希值。 </p><p>Swift 会在每次运行时 为 <code>Dictionary</code> 和 <code>Set</code> 提供一个随机的种子去产生随机数作为哈希的参数，所以 <code>Dictionary</code> 和 <code>Set</code> 都不再是一个有序的集合，如果你的代码里依赖于它们的顺序的话，那就会产生问题了。</p><p>而如果你希望使用一个自定义的随机种子的话，可以使用环境变量 <code>SWIFT_DETERMINISTIC_HASHING</code> 去控制：</p><p><img src="/images/Screen%20Shot%202018-06-09%20at%2021.15.25.webp" alt="Screen Shot 2018-06-09 at 21.15.25"></p><p>更多细节可以查看 <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0206-hashable-enhancements.md#hasher">SE-0206</a> 提案，不是很长，建议大家阅读一遍。</p><h4 id="随机数产生"><a href="#随机数产生" class="headerlink" title="随机数产生"></a>随机数产生</h4><p>随机数的产生是一个很大的话题，通常它都需要系统去获取运行环境中的变量去做为随机种子，这也造就了不同平台上会有不同的随机数 API：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">#if</span> os(iOS) <span class="operator">||</span> os(tvOS) <span class="operator">||</span> os(watchOS) <span class="operator">||</span> os(macOS)</span><br><span class="line">    <span class="keyword">return</span> <span class="type">Int</span>(arc4random())</span><br><span class="line"><span class="keyword">#else</span></span><br><span class="line">    <span class="keyword">return</span> random()</span><br><span class="line"><span class="keyword">#endif</span></span><br></pre></td></tr></table></figure><p>但开发者不太应该去关系这些这么琐碎的事情，虽然 Swift 4.2 里最重要的是 ABI 兼容性的提升，但还是实现了一套随机数的 API：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> randomIntFrom0To10 <span class="operator">=</span> <span class="type">Int</span>.random(in: <span class="number">0</span> <span class="operator">..&lt;</span> <span class="number">10</span>)</span><br><span class="line"><span class="keyword">let</span> randomFloat <span class="operator">=</span> <span class="type">Flow</span>.random(in: <span class="number">0</span> <span class="operator">..&lt;</span> <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> greetings <span class="operator">=</span> [<span class="string">&quot;hey&quot;</span>, <span class="string">&quot;hi&quot;</span>, <span class="string">&quot;hello&quot;</span>, <span class="string">&quot;hola&quot;</span>]</span><br><span class="line"><span class="built_in">print</span>(greetings.randomElement()<span class="operator">!</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> randomlyOrderGreetings <span class="operator">=</span> greetings.shuffled()</span><br><span class="line"><span class="built_in">print</span>(randomlyOrderedGreetings)</span><br></pre></td></tr></table></figure><p>我们现在可以简单地获取一个随机数，获取数组里的一个随机元素，或者是把数组打乱，在苹果的平台上或者是 Linux 上随机数的产生都是安全的。</p><p>并且你还可以自己定义一个随机数产生器：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">CustomRandomNumberGenerator</span>: <span class="title class_ inherited__">RandomNumberGenerator</span> &#123; <span class="operator">...</span> &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> generator <span class="operator">=</span> <span class="type">CustomRandomNumberGenerator</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> randomIntFrom0To10 <span class="operator">=</span> <span class="type">Int</span>.random(in: <span class="number">0</span> <span class="operator">..&lt;</span> <span class="number">10</span>, using: <span class="operator">&amp;</span>generator)</span><br><span class="line"><span class="keyword">let</span> randomFloat <span class="operator">=</span> <span class="type">Flow</span>.random(in: <span class="number">0</span> <span class="operator">..&lt;</span> <span class="number">1</span>, using: <span class="operator">&amp;</span>generator)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> greetings <span class="operator">=</span> [<span class="string">&quot;hey&quot;</span>, <span class="string">&quot;hi&quot;</span>, <span class="string">&quot;hello&quot;</span>, <span class="string">&quot;hola&quot;</span>]</span><br><span class="line"><span class="built_in">print</span>(greetings.randomElement(using: <span class="operator">&amp;</span>generator)<span class="operator">!</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> randomlyOrderGreetings <span class="operator">=</span> greetings.shuffled(using: <span class="operator">&amp;</span>generator)</span><br><span class="line"><span class="built_in">print</span>(randomlyOrderedGreetings)</span><br></pre></td></tr></table></figure><h4 id="检测目标运行平台"><a href="#检测目标运行平台" class="headerlink" title="检测目标运行平台"></a>检测目标运行平台</h4><p>以往我们自定义一些跨平台的代码的时候，都是这么判断的：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">#if</span> os(iOS) <span class="operator">||</span> os(watchOS) <span class="operator">||</span> os(tvOS)</span><br><span class="line">    <span class="keyword">import</span> UIKit</span><br><span class="line">    <span class="keyword">typealias</span> <span class="type">Color</span> <span class="operator">=</span> <span class="type">UIColor</span></span><br><span class="line"><span class="keyword">#else</span></span><br><span class="line">    <span class="keyword">import</span> AppKit</span><br><span class="line">    <span class="keyword">typealias</span> <span class="type">Color</span> <span class="operator">=</span> <span class="type">NSColor</span></span><br><span class="line"><span class="keyword">#endif</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">Color</span> &#123; <span class="operator">...</span> &#125;</span><br></pre></td></tr></table></figure><p>但实际上我们关心的并不是到底我们的代码能跑在什么平台上，而是它能导入什么库，所以 Swift 4.2 新增了一个判断库是否能导入的宏：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">#if</span> canImport(<span class="type">UIKit</span>)</span><br><span class="line">    <span class="keyword">import</span> UIKit</span><br><span class="line">    <span class="keyword">typealias</span> <span class="type">Color</span> <span class="operator">=</span> <span class="type">UIColor</span></span><br><span class="line"><span class="keyword">#elseif</span> canImport(<span class="type">AppKit</span>)</span><br><span class="line">    <span class="keyword">import</span> AppKit</span><br><span class="line">    <span class="keyword">typealias</span> <span class="type">Color</span> <span class="operator">=</span> <span class="type">NSColor</span></span><br><span class="line"><span class="keyword">#else</span></span><br><span class="line">    <span class="keyword">#error</span>(<span class="string">&quot;Unsupported platform&quot;</span>)</span><br><span class="line"><span class="keyword">#endif</span></span><br></pre></td></tr></table></figure><p>并且 Swift 还新增了一套编译宏能够让我们在代码里手动抛出编译错误 <code>#error(&quot;Error&quot;)</code> 或者是编译警告 <code>#warn(&quot;Warning&quot;)</code>（以后不再需要 FIXME 这种东西了）。</p><p>另外还增加了一套判断运行环境的宏，下面是我们判断是否为模拟器环境的代码：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Swift 4.2 以前</span></span><br><span class="line"><span class="keyword">#if</span> (os(iOS) <span class="operator">||</span> os(watchOS) <span class="operator">||</span> os(tvOS) <span class="operator">&amp;&amp;</span></span><br><span class="line">    (cpu(i396) <span class="operator">||</span> cpu(x86_64))</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line"><span class="keyword">#endif</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Swift 4.2</span></span><br><span class="line"><span class="keyword">#if</span> hasTargetEnviroment(simulator)</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line"><span class="keyword">#endif</span></span><br></pre></td></tr></table></figure><h4 id="废除-ImplicityUnwrappedOptional-类型"><a href="#废除-ImplicityUnwrappedOptional-类型" class="headerlink" title="废除 ImplicityUnwrappedOptional 类型"></a>废除 ImplicityUnwrappedOptional 类型</h4><p><code>ImplicityUnwrappedOptional</code> 又被称为强制解包可选类型，它其实是一个非必要的工具，我们使用它最主要的目的是，减少显式的解包，例如说 <code>UIViewController</code> 的生命周期里， <code>view</code> 在 <code>init</code> 的时候是一个空值，但是只要 <code>viewDidLoad</code> 之后就会一直存在，如果我们每次都使用都需要手动显式强制解包 <code>view!</code> 就会很繁琐，使用了 IUO 就可以节省这一部分解包代码。</p><p>所以 <code>ImplicityUnwrappedOptional</code> 是与 Objective-C 的 API 交互时很有用的一个工具，所有未被标记上 nullability 的变量都会被作为 IUO 类型暴露给 Swift，它的出现同时也是为了暂时填补 Swift 里语言的未定义部分，去处理那些固定模式的代码。随着语言的发展，我们应该明确 IUO 的作用，并且用好的方式去取代它。</p><p><a href="https://github.com/apple/swift-evolution/blob/master/proposals/0054-abolish-iuo.md">SE-0054</a> 提案就是为此而提出的，这个提案实际上在 Swift 3 里就实现了一部分了，在 Swift 4.2 里继续完善并且完整得实现了出来。</p><p>以往我们标记 IUO 的时候，都是通过<strong>类型</strong>的形式去实现，在 Swift 4.2 之后，IUO 不再是一个类型，而是一个标记，编译器会通过给<strong>变量</strong>标记上 <code>@_autounwrapped</code> 去实现，所有被标记为 IUO 的变量都由编译器在编译时进行隐式强制解包：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> x: <span class="type">Int</span>! <span class="operator">=</span> <span class="number">0</span> <span class="comment">// x 被标记为 IUO，类型其实还是 Optional&lt;Int&gt;</span></span><br><span class="line"><span class="keyword">let</span> y <span class="operator">=</span> x <span class="operator">+</span> <span class="number">1</span>   <span class="comment">// 实际上编译时，编译器会转化为 x! + 1 去进行编译</span></span><br></pre></td></tr></table></figure><p>这就更加符合我们的原本的目的，因为我们需要标记的是<strong>变量</strong>的 nullability，而通过类型去标记的话实际上我们是在给一个<strong>值</strong>标记上 IUO，而并非是<strong>变量</strong>。</p><p>当然，这样的改变也会给之前的代码带来影响，因为我们标记的对象针对的是变量，而并非类型，所以以往作为类型存在的 IUO 就会变成非法的声明：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a: [<span class="type">Int</span>!] <span class="operator">=</span> [] <span class="comment">// 编译不通过</span></span><br></pre></td></tr></table></figure><h4 id="内存独占访问权"><a href="#内存独占访问权" class="headerlink" title="内存独占访问权"></a>内存独占访问权</h4><p>同一时间内，代码对于某一段内存空间的访问是具有独占性，听起来很难懂是吧，举个例子你就明白了，在遍历数组的同时对数组进行修改：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> a <span class="operator">=</span> [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> number <span class="keyword">in</span> a &#123;</span><br><span class="line">    a.append(number) <span class="comment">// 产生未定义的行为</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Swift 通过内存独占访问权的模型，可以在编译时检测出这种错误，在 Swift 4.2 里得到加强，可以检测出更多非法内存访问的情况，并且提供了运行时的检查，在未来，内存独占访问权的检查会像数组越界一样默认开启：</p><p><img src="/images/Screen%20Shot%202018-06-10%20at%2000.53.48.webp" alt="Screen Shot 2018-06-10 at 00.53.48"></p><h4 id="推荐资源"><a href="#推荐资源" class="headerlink" title="推荐资源"></a>推荐资源</h4><p>推荐查看Ole Begemann 大神的出品的 <a href="https://github.com/ole/whats-new-in-swift-4-2">What’s new in Swift 4.2</a>，带着大家用 Playground 亲身体会一下 Swift 里新的语法功能。</p><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>Swift 5 是一个很重要的里程碑，ABI 的稳定意味着这一份设计需要支撑后面好几个大版本的功能需求，延期我觉得不算是一件坏事。待 ABI 尘埃落定之后，Swift 的语法功能肯定还会有一波爆发，async&#x2F;await，原生的正则表达式…，都让我更加期待 2019 年的 Swift 5。</p>]]>
    </content>
    <id>https://kemchenj.github.io/2018-07-10/</id>
    <link href="https://kemchenj.github.io/2018-07-10/"/>
    <published>2018-07-09T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>这个 Session 分为两个部分，前半部分会简单介绍一下 Swift 开源相关的事情，后半部分我们深入了解一下 Swift 4.2 带来了哪些更新。</p>
<h2 id="社区的发展"><a href="#社区的发展" class="headerlink" title="社区的发展"></a>社区的发展</h2><p>首先我们来看一下 Swift 的一些统计数据，Swift 自开源之后，总共有 600 个代码贡献者，合并了超过 18k pull request。</p>
<p><img src="/images/Screen%20Shot%202018-06-10%20at%2012.41.12.webp" alt="Screen Shot 2018-06-10 at 12.41.12"></p>]]>
    </summary>
    <title>WWDC 2018 - What's New in Swift?</title>
    <updated>2026-09-02T11:08:19.191Z</updated>
  </entry>
  <entry>
    <author>
      <name>四娘</name>
    </author>
    <content>
      <![CDATA[<p>Codable 作为 Swift 的特性之一也是很注重安全，也很严谨，但它对于“严谨”和“安全”的定义不一定跟别的语言一样，这就导致了它在实际使用时总会有这样那样的磕磕绊绊，我们不得不重写 init 方法去让它跟外部环境融洽地共存。最近在工作中这样的事情发生多了，我也就不得不想办法去解决它。</p><span id="more"></span><h2 id="严格的类型解析"><a href="#严格的类型解析" class="headerlink" title="严格的类型解析"></a>严格的类型解析</h2><p>最开始遇到了第一个问题就是 <code>Bool</code> 的解析，我们后端的接口习惯使用 <code>0</code> 跟 <code>1</code> 整数去表达布尔值，解析失败之后，我第一感觉是这会不会是个 bug，所以去翻了一下 <code>JSONDecoder</code> 的源码：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">func</span> <span class="title function_">unbox</span>(<span class="keyword">_</span> <span class="params">value</span>: <span class="keyword">Any</span>, <span class="params">as</span> <span class="params">type</span>: <span class="type">Bool</span>.<span class="keyword">Type</span>) <span class="keyword">throws</span> -&gt; <span class="type">Bool</span>? &#123;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">let</span> number <span class="operator">=</span> value <span class="keyword">as?</span> <span class="type">NSNumber</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> number <span class="operator">===</span> kCFBooleanTrue <span class="keyword">as</span> <span class="type">NSNumber</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> number <span class="operator">===</span> kCFBooleanFalse <span class="keyword">as</span> <span class="type">NSNumber</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="operator">...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果把 <code>===</code> 改成 <code>==</code> 就可以很好地解决我的问题，我本来还很天真得以为这真的是个 bug，但在 Twitter 上向开发组的人<a href="https://twitter.com/kemchenj/status/930722213887492096">求证</a>之后，他们表示代码并没有错，就是这么设计的，Boolean 就是 Boolean，Int 就是 Int，不应该混到一起用。</p><p>还有一个比较棘手的问题，<code>URL</code> 的 <code>init?(string:)</code> 在传入<strong>空字符串</strong>的时候会初始化失败，所以在把空字符串解析为 <code>URL</code> 的时候会直接中断整个解析然后抛出错误，还有一个就是数组内部存在 <code>null</code> 元素的时候，如果 <code>Array</code> 的元素不声明为 <code>Optional</code> 的话也是会中断解析。</p><h2 id="Swizzle-掉-decode-方法"><a href="#Swizzle-掉-decode-方法" class="headerlink" title="Swizzle 掉 decode 方法"></a>Swizzle 掉 decode 方法</h2><p>比起重新自定义一个 Decoder 来说，如果能够 swizzle 掉 decode 方法，直接控制 decode 行为会更加方便。实际上我们真的可以做到，Codable 的原理是自动代码生成，严格来说，它其实不算是编译的一部分：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">Foo</span>: <span class="title class_ inherited__">Codable</span> &#123;</span><br><span class="line">    <span class="keyword">var</span> bar: <span class="type">Int</span>?</span><br><span class="line"></span><br><span class="line">    <span class="comment">// &lt;--自动生成的部分</span></span><br><span class="line">    <span class="keyword">init</span>(<span class="params">from</span> <span class="params">decoder</span>: <span class="type">Decoder</span>) <span class="keyword">throws</span> &#123;</span><br><span class="line">        <span class="keyword">let</span> container <span class="operator">=</span> decoder.container(keyedBy: <span class="type">CodingKeys</span>.<span class="keyword">self</span>)</span><br><span class="line">        bar <span class="operator">=</span> container.decodeIfPresent(<span class="type">Int</span>.<span class="keyword">self</span>, forKey: .bar)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 自动生成的部分--&gt;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>并且 <code>decodeIfPresent</code> 方法是在 Foundation 框架里的，那么我们能不能在我们的 Module 里也写一个 <code>decodeIfPresent</code> 方法重载掉它呢？因为如果方法是在 extension 里声明并实现的话，方法会优先从 Module 内部开始查找，那就尝试一下：</p><p><img src="/images/Screen%20Shot%202018-07-09%20at%2020.36.15.webp" alt="Screen Shot 2018-07-09 at 20.36.15"></p><p>成功了，那么就回到我们最初的目的，把 <code>URL</code> 和 <code>Bool</code> 也重载掉：</p><p><img src="/images/Screen%20Shot%202018-07-09%20at%2020.41.40.webp" alt="Screen Shot 2018-07-09 at 20.41.40"></p><p>并且这种重载的方法是用的是直接派发，所以我们可以控制这个函数的作用范围：</p><figure class="highlight swift"><table><tr><td class="code"><pre><span class="line"><span class="comment">// A 文件</span></span><br><span class="line"><span class="keyword">extension</span> <span class="title class_">KeyedDecodingContainer</span> &#123;</span><br><span class="line">    <span class="keyword">fileprivate</span> <span class="keyword">func</span> <span class="title function_">decodeIfPresent</span>(<span class="keyword">_</span> <span class="params">type</span>: <span class="type">Int</span>.<span class="keyword">Type</span>, <span class="params">forKey</span> <span class="params">key</span>: <span class="type">CodingKey</span>) -&gt; <span class="type">Int</span>? &#123; <span class="operator">...</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// B 文件</span></span><br><span class="line"><span class="comment">// 这里不会调用到 A 文件里的方法</span></span><br><span class="line"><span class="keyword">let</span> b <span class="operator">=</span> container.decodeIfPresent(<span class="type">Int</span>.<span class="keyword">self</span>, forKey: key)</span><br></pre></td></tr></table></figure><p>甚至我们可以在 Module 内重载一遍，应对个别特殊情况可以在文件里再重载一遍，达到最佳的灵活度，从某种程度上来说，我认为这甚至是比 Objective-C 的消息机制更加灵活的一种函数声明机制，而且它的影响范围是有限的，不容易对外部模块造成破坏（别声明为 <code>open</code> 或者 <code>public</code> 就没问题）。</p><p>我对于 Twitter 上 Swift 开发团队的成员发的一条推印象特别深，他说其实 Swift 也有 Selector 和 IMP 的机制，只不过这个方法选择的过程是在编译时去完成，而并非在运行时去完成的。通过了解方法选择的规则，就可以做到类似于 Swizzle 的效果，这也是 Swift 重载机制有趣而且复杂的地方。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>现在大家可以通过这种方法去重构掉项目里那些多余的 <code>init(from:)</code> 函数啦！🎉🎉🎉</p>]]>
    </content>
    <id>https://kemchenj.github.io/2018-07-09/</id>
    <link href="https://kemchenj.github.io/2018-07-09/"/>
    <published>2018-07-08T16:00:00.000Z</published>
    <summary>
      <![CDATA[<p>Codable 作为 Swift 的特性之一也是很注重安全，也很严谨，但它对于“严谨”和“安全”的定义不一定跟别的语言一样，这就导致了它在实际使用时总会有这样那样的磕磕绊绊，我们不得不重写 init 方法去让它跟外部环境融洽地共存。最近在工作中这样的事情发生多了，我也就不得不想办法去解决它。</p>]]>
    </summary>
    <title>或许你并不需要重写 init(from:) 方法</title>
    <updated>2026-09-02T11:08:19.191Z</updated>
  </entry>
</feed>
