• 作者:老汪软件技巧
  • 发表时间:2024-09-26 11:01
  • 浏览量:

在 App Store Connect 中创建回头客优惠时,你需要确定顾客资格,选择地区提供情况,并选取折扣类型。从今年秋季开始, 回头客优惠将向符合条件的顾客显示。

提案通过的提案

SE-0443 精确控制编译器警告的标志 提案通过审查。该提案已在 第六十一期周报 正在审查的提案模块做了详细介绍。

正在审查的提案

SE-0444 成员导入可见性 提案正在审查。

在 Swift 中,有一些规则决定了是否会将另一个模块中的声明名称视为当前作用域的一部分。例如,如果你使用了 swift-algorithms 包,并且想要使用全局函数 chain(),那么你必须在引用该函数的文件中写上 import Algorithms,否则编译器会认为它超出了作用域:

// 缺少 'import Algorithms'
let chained = chain([1], [2]) // 错误:找不到 'chain' 的作用域

不过,对于成员声明(例如在结构体中声明的方法),其可见性规则却有所不同。当解析成员声明时,即使引入该成员的模块只是通过传递方式导入,成员也会处于作用域内。传递导入的模块可以是在另一个源文件中直接导入的模块,也可以是程序某个直接依赖项的依赖。这种不一致性可以理解为一个微妙的漏洞,而不是有意的设计决策,在很多 Swift 代码中它可能不会引起注意。

然而,当涉及到扩展的成员时,导入规则变得更令人惊讶,因为扩展和其名义类型(nominal type)可以在不同的模块中声明。

该提案通过更改规则,统一了名称查找的行为,使顶级声明和成员使用相同的标准进入作用域。

SE-0445 改进 String.Index 的打印描述 提案正在审查。

此提案符合 String.Index 的 CustomStringConvertible。

Swift论坛讨论SwiftIfConfig 库正在取代编译器的 #if 处理

内容大概:

Swift 编译器正在经历一项重大更新,新的 SwiftIfConfig 库将取代编译器中对 #if 指令的处理。这个库是 swift-syntax 包的一部分,目前已完成多个关键改进:

配置区域的实现:为 IDE 中的 #if 折叠功能提供支持,增强代码覆盖率分析。替换 C++ 解析器中的 #if 条件逻辑:使大部分旧的 ParseIfConfig.cpp 代码不再需要,优化了对 #if 条件的解析。在 ASTGen 中支持 #if 指令:新解析器通过支持 #if,提升了处理复杂语法结构的能力。基于 #if 条件输出语法错误:根据 #if 指令的配置情况来决定哪些语法错误需要打印。删除遗留的“内联文本”提取逻辑:进一步简化了编译器中的代码。

接下来的工作是从 C++ 语义 AST 中彻底移除 IfConfigDecl,这一改变不仅能使编译器代码更简洁,还能提高 #if 在不同语法规则中的扩展性。此外,这次更新还会保留一些重要的编译器行为,例如在不活跃的 #if 块中抑制变量未使用的警告,以及抑制 try 和 throw 相关的警告。

在 SourceKit 方面,多个查询已被 swift-syntax 和 swift-format 工具取代,更新会废弃旧的查询并在下一个 Swift 版本中完全移除这些处理#if的查询。与此类似,Swift 编译器前端的 swift-indent 模式也将被移除,因其功能有限,且已有更现代的工具(如 swift-format)可供使用。

总体而言,这次更新将 Swift 编译器中的 #if 处理逻辑迁移到 swift-syntax 库中,极大简化了主编译器的代码基础,标志着一大进步。

提议重新审视允许更多非标识符字符的反引号分隔标识符

内容大概:

本文提出再次允许在反引号(backticks)中使用包含空格和其他非标识符字符的标识符。过去曾提出过类似的提案(SE-0275),但被拒绝。本文试图基于新的信息和使用经验重新审视该提案。

描述性测试命名:

之前的拒绝理由之一是希望测试框架能够提供不同的方法来为测试用例附加字符串。虽然新的 swift-testing 框架已经实现了这一点,但当前的方法要求用户为测试命名两次,这不仅冗余,还引入了不一致。例如:

@Test("tapping pushes the nav stack")
func tappingPushesTheNavStack() {
    // 测试代码...
}

这种方法导致测试报告和测试 UI 中使用描述性名称,但调试器、回溯以及代码导航工具仍使用函数声明名。为了避免这种不一致,本文建议允许函数名直接使用描述性名称,例如:

@Test func `tapping pushes the nav stack`() {
    // 测试代码...
}

模块命名:

在大型代码库中,模块命名是一个挑战。当前的做法是将模块构建目标路径转换为有效的标识符名,例如:

import my_cool_project_ui_navigation

但这种做法增加了自动化工具的复杂性,特别是在处理依赖关系和导入管理时。本文建议允许直接使用包含非标识符字符的路径作为模块名,例如:

import `my/cool/project/ui/navigation`

这将简化导入语句并减少自动化工具的复杂性。

其他注意事项:

提案还讨论了与工具链的边缘情况和潜在的未来方向。例如,建议通过定义一个字符集来限制反引号中的允许字符,以增强对未来 Unicode 扩展的适应性。

总结:该提案旨在简化代码中的测试命名和模块命名,同时减少不必要的复杂性,并提高代码的可读性和一致性。

讨论如果没有办法拦截“fatalError”,则会对服务器造成危害

内容大概:

在服务器端使用Swift时,fatalError 无法被拦截是有害的。虽然通过将功能隔离到Docker容器中,并自动重启失败的容器,可以在一定程度上缓解问题,但在生产环境中,代码无论多么理想化,都会因各种原因导致崩溃。Swift早期编译器版本中的无效代码、缺失库调用导致的致命错误、内存泄漏等问题,都可能导致容器崩溃。

有两种应对严重错误的思路:一种是类似Erlang的“快速失败”方法,失败时生成新的轻量级进程再次执行代码;另一种是更常见的方法,假设大多数异常不会破坏内存,程序可以继续运行。fatalError() 的设计假设这些错误不符合程序模型,并且假设大多数代码在隔离的进程中运行。这种方法是否适用于服务器端的Swift,尤其是在使用结构化并发时,仍然有待验证。

_苹果、华为“撞档”上新 | Swift 周报 issue 62_苹果、华为“撞档”上新 | Swift 周报 issue 62

总的来说,Swift需要根据不同的应用场景来调整对严重错误的处理策略,特别是在服务器环境下。

讨论并行计算 DAG/共享未来?

内容大概:

本文讨论了如何在 Swift 中并行化计算任务。具体任务可以描述为以下函数:

func compute(_ input: [Key]) -> [Key: Value]

该函数的结果包含所有输入中的键,还会生成一些在计算过程中发现的额外键。存在一个依赖有向无环图(DAG),描述所有值之间的依赖关系。

通过一个简单的例子展示了计算的形式,例如计算整数n的阶乘并将其映射到字典中:

func compute(_ input: [Int]) -> [Int: Int] {
    var r: [Int: Int] = [:]
    func fact(_ x: Int) -> Int {
        if let y = r[x] { return y; }
        let y = x == 0 ? 1 : x * fact(x - 1)
        r[x] = y
        return y
    }
    for z in input { _ = fact(z) }
    return r
}

在这个讨论中提出了一种可能的并行化方案:在最终的for循环中为每个输入元素启动并行任务。同时,考虑到DAG的结构,一个键的值的计算可能依赖于另一个键的值,如果该键的值已经在另一个线程中开始计算,那么该计算可能会暂停等待。这种情况类似于共享Future系统。

此外,作者希望在每个线程中批量处理计算结果,并且只在批量足够大时将结果合并到最终结果中,以减少同步的开销。同时,允许某些键值对被重复计算,以避免使用共享的Future系统。

本次的讨论希望通过讨论找到合适的并行计算解决方案。

讨论关于“间接枚举”语义的澄清

内容大概:

最近作者在工作项目中建议将一些枚举标记为 indirect,以减少它们占用的栈内存。然而,在解释 indirect 实际上做了什么时,我发现很难清楚地说明,除了引用编译器内部机制之外。

官方文档中关于 indirect enum 的唯一提及是“递归枚举”,这是最常见的使用场景。然而,还有其他有效的使用场景,论坛上也经常讨论这些,但从官方文档来看,这是否是预期用途并不明确。

作者认为值得更明确地记录 indirect 在底层实际做了什么,并提到它在内存优化场景中的作用。虽然语言本身从未“官方”保证某些内容的内存分配方式,但了解其在不同场景下的行为会有所帮助。

例如,一个常见的场景是链表类型使用 indirect enum 实现。编译器可能足够智能,可以证明某个链表在函数内不逃逸,从而将堆分配转化为动态栈分配。

另一个例子是全局常量的值是 indirect enum,目前生成的代码会执行堆分配,但理论上可以将这些间接情况放入静态空间。

此外,由于 indirect 的情况是不可变的,因此不能对这些盒子的引用标识做出任何保证,除非使用不安全的技巧来观察其标识。

推荐博文

在 Swift 中引入对 Oblivious HTTP 的支持

摘要: 这篇官方文章介绍了 Swift 中对 Oblivious HTTP(OHTTP)的支持,并发布了新的 SwiftNIO 包 SwiftNIO Oblivious HTTP。Oblivious HTTP 通过加密 HTTP 请求并结合第三方中继服务,保护客户端的身份信息,增强隐私性,避免暴露诸如 IP 地址等数据。

SwiftNIO Oblivious HTTP 包提供两个主要库:

ObliviousHTTP:实现了 RFC 9292 中定义的二进制 HTTP 编码方案和 Oblivious HTTP。ObliviousX:提供加密功能的 API,支持 Oblivious HTTP 及其他数据的加密。文章通过代码示例演示了如何使用这些库进行 HTTP 消息的序列化、反序列化和加密解密。还提到了未来的开发计划,包括与 SwiftNIO 更好的集成、对其他 Swift 类型的支持,以及对分块 OHTTP 的支持。

SwiftNIO Oblivious HTTP 仍处于早期开发阶段,期待社区的反馈和贡献。。

Swift 中的任务和任务组

摘要: 这篇博客介绍了 Swift 中的任务(Task)和任务组(Task Group),并讲解了它们的使用方法及相关高级技巧。

任务(Task) 是 Swift 并发编程的一部分,允许在非并发环境中创建并发任务,任务在创建后立即运行。文章展示了如何创建任务、处理任务的错误和取消任务。Swift 提供了 Task.checkCancellation() 主动抛出错误终止任务,或通过 Task.isCancelled 检查任务是否被取消。还介绍了如何设置任务优先级和任务的生命周期状态(运行中、暂停、取消和完成)。

任务组(Task Group) 用于组合并发执行的多个任务,并等待所有任务完成后返回结果。通过 withTaskGroup 或 withThrowingTaskGroup 可以创建任务组,并发执行任务。文章提供了如何处理任务组中的错误、如何避免数据竞争,并展示了取消任务组的用法。

最后,作者强调了避免并发修改任务组的操作,推荐使用 cancelAll() 来取消任务组中的所有子任务,以及 addTaskUnlessCancelled() 来确保任务组未被取消时才添加新任务。。

递归枚举在 Swift 中的妙用

摘要: 这篇博客介绍了 Swift 中递归枚举的使用及其优势。递归枚举允许枚举的某些情况包含自身实例,适用于建模层次化或递归结构的数据,如文件系统。通过 indirect 关键字,Swift 可以安全地处理递归引用,避免内存问题。

文章首先展示了如何用递归枚举实现文件系统模型,并引入了文件、文件夹和别名的概念。然后通过代码示例,展示了如何使用递归枚举创建嵌套文件结构,并递归计算文件夹中的总项目数。

此外,文章还解释了在引用自身时如何正确使用 indirect 关键字,并指出当引用通过集合类型(如数组)实现时,不需要 indirect 标记。

话题讨论

近期苹果公司推出了iPhone 16系列智能手机和苹果手表等新品。华为公司发布了全球首款三折叠屏手机,在铰链系统、屏幕弯折等方面实现多项技术突破,展示了功能和美学的融合。有人说智能手机的霸主地位要换主了,你觉得的呢?

1.华为也不过是一个高个子小兵罢了,想比肩苹果,可笑可笑。2.苹果手机近年来创新科技不足,被华为超越是迟早的事。3.于消费者而言,霸主更替并无坏处,品牌产商有足够的压力和动力才能创造更好的产品。

关于我们

Swift社区是由 Swift 爱好者共同维护的公益组织,我们在国内以微信公众号的运营为主,我们会分享以 Swift实战、SwiftUl、Swift基础为核心的技术内容,也整理收集优秀的学习资料。

特别感谢 Swift社区 编辑部的每一位编辑,感谢大家的辛苦付出,为 Swift社区 提供优质内容,为 Swift 语言的发展贡献自己的力量。