9 月 13 日,2025 Inclusion・外滩大会「开源嘉年华」正在限量报名中! 了解详情
写点什么

Swift 5 新特性详解:ABI 稳定终于来了!

  • 2019-01-28
  • 本文字数:5203 字

    阅读完需:约 17 分钟

Swift 5新特性详解:ABI 稳定终于来了!

近日,苹果开发者博客更新了一篇关于 Swift 5 的文章,带来了 Swift 5 新特性的消息,其中最受开发期待的莫过于 iOS 12.2 将带来 ABI 稳定性,这意味着基础库将植入系统中,不再包含在 App 中,应用程序的体积会更小,更多新功能请看下文。

App 瘦身

新功能

Swift 应用程序不再包含用于 Swift 标准库和 Swift SDK(运行 iOS 12.2、watchOS 5.2 和 tvOS 12.2 的设备的构建变体)的动态链接库。因此,在使用 TestFlight 进行测试时,或者为本地开减小应用程序体积时,Swift 应用程序可以变得更小。


要查看 iOS 12.2 和 iOS 12.1(或更早版本)应用程序之间的文件大小差异,请将应用程序的部署目标设置为 iOS 12.1 或更早版本,将 scheme 设置为 Generic iOS Device,然后创建应用程序压缩包。


在构建好压缩包之后,从压缩包管理器中选择 Distribution App,然后选择 Development Distribution。确保在 App Thinning 下拉菜单中选择特定的设备,比如 iPhone XS。这个过程完成后,在新创建的文件夹中打开 App Thinning Size Report。iOS 12.2 的体积会比 iOS 12.1 或更早版本的体积小。具体的大小差异取决于应用程序使用的框架的数量。

Swift

@dynamicCallable 属性允许你调用命名的类型,就像使用简单的语法糖调用函数一样。主要的应用场景是动态语言互操作性。


例如:


@dynamicCallable struct ToyCallable {    func dynamicallyCall(withArguments: [Int]) {}    func dynamicallyCall(withKeywordArguments: KeyValuePairs<String, Int>) {}}
let x = ToyCallable()
x(1, 2, 3)// Desugars to `x.dynamicallyCall(withArguments: [1, 2, 3])`
x(label: 1, 2)// Desugars to `x.dynamicallyCall(withKeywordArguments: ["label": 1, "": 2])`
复制代码


现在支持标识 KeyPath(.self),一个引用其整个输入值的WritableKeyPath


let id = \Int.selfvar x = 2print(x[keyPath: id]) // Prints "2"x[keyPath: id] = 3print(x[keyPath: id]) // Prints "3"
复制代码


在 Swift 5 之前,你可以编写一个带有可变参数的枚举:


enum X {    case foo(bar: Int...) }
func baz() -> X { return .foo(bar: 0, 1, 2, 3) }
复制代码


现在如果这么做会出错。相反,现在参数改成了一个数组,并且需要显式传入数组:


enum X {    case foo(bar: [Int]) } 
func baz() -> X { return .foo(bar: [0, 1, 2, 3]) }
复制代码


在 Swift 5 模式下,可以用?和 Optional 类型表达式来扁平化生成的 Optional,而不是返回嵌套的 Optional。


如果类型 T 符合这些字面量初始化中的一个——例如 ExpressibleByIntegerLiteral——并假设 literal 是一个字面量表达式,那么 T(literal)就创建了一个 T 类型的字面量。


例如,UInt64(0xffff_ffff_ffff_ffff)现在是有效的,而之前它们会导致默认整型字面量类型 Int 溢出。


字符串插值的性能、清晰度和效率得到了改进。


旧的_ExpressibleByStringInterpolation 协议被移除,如果你的代码使用了这个协议,需要更新这些代码,你可以使用 #if 在 Swift 4.2 和 Swift 5 之间条件化代码。例如:


#if compiler(<5)extension MyType: _ExpressibleByStringInterpolation { /*...*/ }#elseextension MyType: ExpressibleByStringInterpolation { /*...*/ }#endif 
复制代码

Swift 标准库

  • DictionaryLiteral 类型被重命为 KeyValuePairs。

  • 与 Objective-C 代码桥接的 Swift 字符串现在会在适当的时候从 CFStringGetCStringPtr 返回一个非空值,而且从-UTF8String 返回的指针与字符串的生命周期(而不是最里面的 autorelease pool)相关联。正确的程序应该不会有任何问题,而且还会带来性能方面的提升。但是,它可能会导致以前未经测试的代码暴露出潜在的错误。

  • Sequence 协议不再具有 SubSequence 关联类型。之前返回 SubSequence 的 Sequence 方法现在返回的是具体的类型。例如,suffix(_:)现在返回 Array。


使用 SubSequence 的 Sequence 扩展应该修改为使用具体的类型,或者修改为 Collection 的扩展(此时 SubSequence 仍然可用)。


例如:


extension Sequence {    func dropTwo() -> SubSequence {        return self.dropFirst(2)    }}
复制代码


变为:


extension Sequence {    func dropTwo() -> DropFirstSequence<Self> {         return self.dropFirst(2)    }}
复制代码


或者:


extension Collection {    func dropTwo() -> SubSequence {        return self.dropFirst(2)    }}
复制代码


  • String 结构的原生编码从 UTF-16 切换到 UTF-8,这样提高了 String.UTF8View 的性能(相对于 String.UTF16View)。

Swift 包管理器

  • 现在,在使用 Swift 5 Package.swift 工具版本时,可以声明一些常用的特定于目标的构建设置。新的设置也可以基于平台和构建配置进行条件化。构建设置支持 Swift 和 C 语言定义、C 语言头文件搜索路径、链接库和链接框架。

  • 现在,在使用 Swift 5 Package.swift 工具版本时,可以为 Apple 平台自定义最低部署目标。如果程序包的任何依赖项指定的最小部署目标大于程序包自身的最低部署目标,就会抛出错误。

  • 新的依赖项镜像功能允许顶层包覆盖依赖项 URL。


可以使用以下命令设置镜像:


$ swift package config set-mirror \--package-url <original URL> --mirror-url <mirror URL>
复制代码


  • swift test 命令提供了–enable-code-coverage 标志,它生成的代码覆盖率数据也适用于其他代码覆盖工具。生成的代码覆盖率数据放在//codecov 目录中。

  • Swift 5 不再支持 Swift 3 Package.swift 工具版本。Swift 3 Package.swift 工具版本的软件包应该升级到更新的工具版本。

  • 针对较大的程序包的包管理器操作现在明显更快。

  • Swift 包管理器提供了一个新的–disable-automatic-resolution 标志,当 Package.resolved 条目与 Package.swift 清单文件中指定的依赖项版本不兼容时,该标志会强制包解析失败。在进行持续集成时,如果需要检查包的 Package.resolved 是否已过期,这项功能会非常有用。

  • swift run 命令提供了一个新的——repl 选项,它将启动 Swift REPL,支持导入包的库目标。这样你就可以轻松地试用 API,而无需构建调用该 API 的可执行文件。

Swift 编译器

  • 现在,对于优化(-O 和-Osize)构建,默认情况下在运行时强制执行独占内存访问。违反排他性的程序将在运行时出现“overlapping access”诊断消息。你可以使用命令行标志禁用它:-enforce-exclusivity = unchecked,但这样做可能会导致未定义的行为。运行时违反排他性通常是由于同时访问类属性、全局变量(包括顶层代码中的变量)或通过转义闭包捕获的变量。

  • Swift 3 模式已被删除。-swift-version 标志支持的值为 4、4.2 和 5。

  • 在 Swift 5 模式中,在迭代使用 Objective-C 声明或来自系统框架的枚举时需要处理未知的 case——可能在将来添加的 case,或者可能在 Objective-C 实现文件中私下定义的 case。Objective-C 允许在枚举中存储任意值,只要它们与底层类型匹配即可。可以使用新的 @unknown 来处理这些未知 case,当然也可以使用普通的 default 来处理它们。


如果你已在 Objective-C 中定义了自己的枚举,并且不需要客户端处理未知 case,那么可以使用 NS_CLOSED_ENUM 宏而不是 NS_ENUM。Swift 编译器就会识别出来,不要求在迭代时提供默认 case。


在 Swift 4 和 4.2 模式下,你仍然可以使用 @unknown。如果省略了它并传入了一个未知的值,程序将在运行时出错,这与 Xcode 10.1 中的 Swift 4.2 的行为是一样的。


  • 现在,默认参数打印在 SourceKit 生成的 Swift 模块接口中,而不只是使用占位符。

  • unowned 和 unowned(unsafe)变量现在支持 Optional。

已知问题

  • 如果引用了 UIAccessibility 的成员,Swift 编译器会在进行到“Merge swiftmodule”这个构建步骤时崩溃。构建日志会包含这样一条消息:


Cross-reference to module 'UIKit'... UIAccessibility... in an extension in module 'UIKit'... GuidedAccessError
复制代码


包含 NS_ERROR_ENUM 枚举的其他类型也可能出现这个问题,但 UIAccessibility 是最常见的。


解决方法:使用“Swift Compiler - Code Generation”下的 Whole Module 编译模式选项重新构建,这是大多数发布配置的默认设置。


  • 为了减少 Swift 元数据占用的空间,Swift 中定义的便捷初始化器现在只在调用 Objective-C 中定义的指定初始化器时提前分配对象。在大多数情况下,这对程序没有任何影响,但是如果从 Objective-C 调用便捷初始化器,就会释放+alloc 初始分配的资源。对于不希望发生对象替换的初始化器用户来说,这可能是有问题的。例如,在使用 initWithCoder:时,NSKeyedUnarchiver 的实现可能会不正确,如果它调用了 init(coder:)的 Swift 实现,并且对象图中包含了环。

  • 如果 KeyPath 字面量引用了在 Objective-C 中定义的属性或者在 Swift 中使用 @objc 和动态修饰符定义的属性,那么编译可能会失败,并抛出“unsupported relocation of local symbol ‘L_selector’”的错误,或者 KeyPath 可能无法在运行时生成正确的哈希值。


解决方法:你可以自己定义非 @objc 包装器属性,指向这个 KeyPath。生成的 KeyPath 与引用原始 Objective-C 属性的 KeyPath 不一样,但使用效果是一样的。


  • 某些项目可能会遇到编译时回归问题。

  • Swift 命令行项目在启动时会崩溃,错误为“dyld: Library not loaded”。


解决方法:添加用户自定义的构建设置 SWIFT_FORCE_STATIC_LINK_STDLIB = YES。

已解决的问题

  • 扩展绑定现在支持嵌套类型的扩展,这些类型本身是在扩展内定义的。之前可能会因为声明顺序问题而失败,出现“undeclared type”错误。

  • 在 Swift 5 模式下,返回 Self 的类方法不能再使用返回具体类类型(非 final)的方法来覆盖。这类代码不是类型安全的,需要将它们改掉。


例如:


class Base {     class func factory() -> Self { /*...*/ }} 
class Derived: Base { class override func factory() -> Derived { /*...*/ } }
复制代码


  • 在 Swift 5 模式下,现在明确禁止声明与嵌套类型同名的静态属性,而之前可以在泛型类型的扩展中进行这样的声明。


例如:


struct Foo<T> {}extension Foo {     struct i {}
// Error: Invalid redeclaration of 'i'. // (Prior to Swift 5, this didn’t produce an error.) static var i: Int { return 0 }}
复制代码


  • 现在可以在子类中正确继承具有可变参数的指定初始化器。

  • 在 Swift 5 模式下,@autoclosure 参数不能再被转发给另一个函数调用的 @autoclosure 参数。相反,你必须使用括号显式调用函数值。调用将被包含在一个隐式闭包中,保证了与 Swift 4 模式相同的行为。


例如:


func foo(_ fn: @autoclosure () -> Int) {}func bar(_ fn: @autoclosure () -> Int) {    foo(fn) // Incorrect, `fn` can’t be forwarded and has to be called.    foo(fn()) // OK} 
复制代码


  • 现在完全支持复杂的递归类型定义,包括之前在运行时会导致死锁的类和泛型。

  • 在 Swift 5 模式下,在将 Optional 值转换为通用占位符类型时,编译器在展开值时会更加保守。这种转换结果现在更接近于非通用上下文中获得的结果。


例如:


func forceCast<U>(_ value: Any?, to type: U.Type) -> U {    return value as! U } 
let value: Any? = 42print(forceCast(value, to: Any.self))// Prints "Optional(42)"// (Prior to Swift 5, this would print "42".)
print(value as! Any)// Prints "Optional(42)"
复制代码


  • 协议现在可以将符合类型限定为给定类的子类。支持两种等效形式:


protocol MyView: UIView { /*...*/ }protocol MyView where Self: UIView { /*...*/ } 
复制代码


Swift 4.2 接受了第二种形式,但还没有完全实现,在编译时或运行时偶尔会发生崩溃。


  • 在 Swift 5 模式下,当在自己的 didSet 或 willSet observer 中设置属性时,observer 现在只在 self 上设置属性(不管是隐式的还是显式的)时才会避免被递归调用。


例如:


class Node {    var children = <a href="">Node     var depth: Int = 0 {        didSet {             if depth < 0 {                // Won’t recursively call didSet, because this is setting depth on self.                 depth = 0            } 
// Will call didSet for each of the children, // as this isn’t setting the property on self. // (Prior to Swift 5, this didn’t trigger property // observers to be called again.) for child in children { child.depth = depth + 1 } } }}</a href="">
复制代码


  • 如果你使用 #sourceLocation 将生成文件中的行映射回源代码,那么诊断信息将显示在源文件中而不是生成文件中。

  • 使用泛型类型别名作为 @objc 方法的参数或返回类型不会再生成无效的 Objective-C 标头。


英文原文:


https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_beta_release_notes/swift_5_release_notes_for_xcode_10_2_beta?language=objc


更多内容,请关注前端之巅。



2019-01-28 09:4717001
用户头像

发布了 731 篇内容, 共 474.7 次阅读, 收获喜欢 2008 次。

关注

评论

发布
暂无评论
发现更多内容

为何共享集群的高可用能力被频频称赞,它的机制有何不同?

YashanDB

yashandb 崖山数据库 崖山DB

fasterWhisper和MoneyPrinterPlus无缝集成

程序那些事

工具 AIGC

医院采购堡垒机厂商重点推荐-行云管家

行云管家

网络安全 医院 堡垒机 等保合规

Mac应用快速启动器:Alfred 5 for Mac 激活版

你的猪会飞吗

mac软件下载 Mac破解软件

异构智算,打赢智算时代「牧野之战」

脑极体

AI

重磅 - Github上免费大屏来啦,教你快速搭建

JEECG低代码

报表工具 大屏设计器 数据库可视化 仪表盘设计器

京东商品描述API:返回值的详细解读

技术冰糖葫芦

API Explorer API 编排 api 货币化 API 文档

人工智能|RAG 检索增强生成

霍格沃兹测试开发学社

【IT小知识】什么是工单?什么是工单中心?

行云管家

堡垒机 工单 IT 运维 工单中心

探索网络安全:浅析文件上传漏洞

我再BUG界嘎嘎乱杀

网络安全 漏洞 文件上传

IT外包服务对中小型企业的重要性

Ogcloud

IT外包 IT外包公司 IT外包服务 IT外包企业 IT外包服务商

【漏洞复现】Nodebb 被爆未授权拒绝服务攻击

我再BUG界嘎嘎乱杀

网络安全 漏洞 网安 漏洞复现

京东百万级调度系统(Buffalo)架构解密

京东科技开发者

系统整容纪:慢SQL之独家秘籍,离不可替代型人才更近一步

京东科技开发者

【论文解读】让我们逐步验证

合合技术团队

#科技 LLM模型

人工智能|RAG 检索增强生成

测吧(北京)科技有限公司

中国AI长卷(二):框架立基

脑极体

AI

巧用 API 接口,尽览京东商品详情

Noah

AI写PPT的软件有哪些?这5款备受推崇的AIPPT工具值得推荐!

彭宏豪95

人工智能 PPT 在线白板 办公软件 AI生成PPT

程序员必备开发神器:领取云主机,零码创建专属AI Agent

华为云开发者联盟

人工智能 云主机 大模型 华为云开发者联盟 企业号2024年7月PK榜

数业智能心大陆,你的专属AI心理专家

心大陆多智能体

智能体 AI大模型 心理健康 数字心理

人工智能丨RAG 检索增强生成

测试人

软件测试

企业选择IT外包服务的好处

Ogcloud

IT外包 IT外包公司 IT外包服务 IT外包企业 IT外包服务商

【YashanDB知识库】数据库使用shutdown immediate无响应导致coredump

YashanDB

yashandb 崖山数据库 崖山DB

中小制造业工厂要不要上MES系统

万界星空科技

制造业 生产管理系统 mes 云mes 万界星空科技

论文图谱当如是:Awesome-Graphs用200篇图系统论文打个样

TuGraphAnalytics

【实战分享】从新手到专家,六大神器加速你的代码人生

敏捷调度TASKCTL

vscode jenkins Postman TASKCTL #docker

远程控制软件怎么选?向日葵功能齐全还不限制免费使用

编程猫

以小见大-B端图标设计沉思

inBuilder低代码平台

设计 图标制作

Swift 5新特性详解:ABI 稳定终于来了!_语言 & 开发_苹果开发者博客_InfoQ精选文章