“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

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:4716549
用户头像

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

关注

评论

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

软件测试/测试开发丨通用 api 封装实战,带你深入理解 PO

测试人

软件测试 自动化测试 测试开发

软件测试/测试开发丨如何高效使用 Requests 做接口自动化测试

测试人

软件测试 自动化测试 接口测试 测试开发 requests

AntDB数据库携超融合流式实时数仓亮相第25届中国高速公路信息技术化大会

亚信AntDB数据库

AntDB AntDB数据库 企业号 4 月 PK 榜

最新版本 Stable Diffusion 开源 AI 绘画工具之使用篇

极客飞兔

人工智能 图文生成 AI绘画 Stable Diffusion

基于SpringBoot实现单元测试的多种情境/方法(二)

天翼云开发者社区

如何通过Java代码将添加页码到PDF文档?

在下毛毛雨

Java PDF 添加页码

社区分享 | Orillusion 引擎入门系列 —— 如何创建一个简单的 3D 示例

Orillusion

WebGL 元宇宙 web3d #WebGPU #开源

重磅消息 | 2023年腾讯云从业者课程全面升级

科技热闻

Excelize 入选 2022 中国开源创新大赛优秀项目

xuri

golang 开源 Go 语言 Excelize OOXML

MobTech MobLink|无码邀请是怎么处理的

MobTech袤博科技

软件测试/测试开发丨两个步骤轻松搞定测试环境问题

测试人

软件测试 自动化测试 测试开发

保证高效写入查询的情况下,如何实现 CPU 资源和磁盘 IO 的最低开销?

TDengine

大数据 tdengine 数据处理 时序数据库 资源消耗

智改数转:这个制造业的必答题该如何作答?

加入高科技仿生人

低代码 数字化 制造业 智能化 智改数转

明道云技术路径选择及与LCDP的比较

明道云

5月上海线下 · CSPO认证周末班【提前报名特惠】“价值交付课程”CST导师亲授

ShineScrum捷行

SketchUp Pro(草图大师2023)中文版 Mac/win

魔仙苹果mac堡

SketchUp Pro 2023 SketchUp Pro中文版 草图大师2023下载

架构训练营模块二作业

请叫我馒头哥丶

架构实战营

为何说低代码平台会提升软件开发效率?

这我可不懂

软件开发 低代码 JNPF

GaussDB(DWS)云原生数仓技术解析

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 4 月 PK 榜

安全可信| 天翼云算力调度平台通过信通院首批可信算力云服务评估!

天翼云开发者社区

算云融合促发展,天翼云以领先云网算力助推数字中国建设!

天翼云开发者社区

Ample Sound Ample Bass Upright III Mac(虚拟立式低音乐器)

魔仙苹果mac堡

联合解决方案|亚信科技AntDB携手蓝凌软件,助推企业数字化办公转型升级

亚信AntDB数据库

AntDB AntDB数据库 企业号 4 月 PK 榜

从“卖船”到提供建造“航母” 的基础设施,用友BIP有底气

用友BIP

分布式政企应用如何快速实现云原生的微服务架构改造

华为云开源

微服务 云原生

不动产行业国产化加速,明源云上榜《中国信创500强》

科技热闻

喜讯!天翼云荣获国际AI顶会ABAW季军

天翼云开发者社区

大咖说丨云计算:数字世界的“中枢神经”

天翼云开发者社区

5月在线 · A-CSM认证周末班【提前报名特惠】“敏捷教练必修课程”CST导师亲授

ShineScrum捷行

Springfox与SpringDoc——swagger如何选择(SpringDoc入门)

天翼云开发者社区

5月上海线下 · CSM认证周末班【提前报名特惠】“全球金牌课程”CST导师亲授

ShineScrum捷行

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