Swift 5新特性详解:ABI 稳定终于来了!
2021-03-10 02:28
标签:pair 泛型类 命令行 test 动态 协议 使用 access keyword 新功能 @dynamicCallable 属性允许你调用命名的类型,就像使用简单的语法糖调用函数一样。主要的应用场景是动态语言互操作性。 现在支持标识 KeyPath(.self),一个引用其整个输入值的 WritableKeyPath(https://developer.apple.com/documentation/swift/writablekeypath?language=objc): 在 Swift 5 之前,你可以编写一个带有可变参数的枚举: 现在如果这么做会出错。相反,现在参数改成了一个数组,并且需要显式传入数组: 在 Swift 5 模式下,可以用? 和 Optional 类型表达式来扁平化生成的 Optional,而不是返回嵌套的 Optional。 变为: 或者: 包含 NS_ERROR_ENUM 枚举的其他类型也可能出现这个问题,但 UIAccessibility 是最常见的。 let value: Any? = 42 print(value as! Any) protocol MyView: UIView { /.../ } class Node { } Swift 5新特性详解:ABI 稳定终于来了! 标签:pair 泛型类 命令行 test 动态 协议 使用 access keyword 原文地址:https://blog.51cto.com/15057848/2567751
译者|薛命灯
编辑|覃云
近日,苹果开发者博客更新了一篇关于 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 struct ToyCallable {
func dynamicallyCall(withArguments: [Int]) {}
func dynamicallyCall(withKeywordArguments: KeyValuePairs
let id = \Int.self
var x = 2
print(x[keyPath: id]) // Prints "2"
x[keyPath: id] = 3
print(x[keyPath: id]) // Prints "3"
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])
}
如果类型 T 符合这些字面量初始化(https://developer.apple.com/documentation/swift/swift_standard_library/initialization_with_literals?language=objc)中的一个——例如 ExpressibleByIntegerLiteral——并假设 literal 是一个字面量表达式,那么 T(literal) 就创建了一个 T 类型的字面量。
例如,UInt64(0xffff_ffff_ffff_ffff) 现在是有效的,而之前它们会导致默认整型字面量类型 Int 溢出。
字符串插值的性能、清晰度和效率得到了改进。
旧的 _ExpressibleByStringInterpolation 协议被移除,如果你的代码使用了这个协议,需要更新这些代码,你可以使用 #if 在 Swift 4.2 和 Swift 5 之间条件化代码。例如:#if compiler(
Swift 标准库
使用 SubSequence 的 Sequence 扩展应该修改为使用具体的类型,或者修改为 Collection 的扩展(此时 SubSequence 仍然可用)。
例如:extension Sequence {
func dropTwo() -> SubSequence {
return self.dropFirst(2)
}
}
extension Sequence {
func dropTwo() -> DropFirstSequence
extension Collection {
func dropTwo() -> SubSequence {
return self.dropFirst(2)
}
}
Swift 包管理器
可以使用以下命令设置镜像:$ swift package config set-mirror --package-url
Swift 编译器
如果你已在 Objective-C 中定义了自己的枚举,并且不需要客户端处理未知 case,那么可以使用 NS_CLOSED_ENUM 宏而不是 NS_ENUM。Swift 编译器就会识别出来,不要求在迭代时提供默认 case。
在 Swift 4 和 4.2 模式下,你仍然可以使用 @unknown。如果省略了它并传入了一个未知的值,程序将在运行时出错,这与 Xcode 10.1 中的 Swift 4.2 的行为是一样的。已知问题
Cross-reference to module ‘UIKit‘
... UIAccessibility
... in an extension in module ‘UIKit‘
... GuidedAccessError
解决方法:使用“Swift Compiler - Code Generation”下的 Whole Module 编译模式选项重新构建,这是大多数发布配置的默认设置。
解决方法:你可以自己定义非 @objc 包装器属性,指向这个 KeyPath。生成的 KeyPath 与引用原始 Objective-C 属性的 KeyPath 不一样,但使用效果是一样的。
解决方法:添加用户自定义的构建设置 SWIFT_FORCE_STATIC_LINK_STDLIB = YES。
已解决的问题
例如:class Base {
class func factory() -> Self { /*...*/ }
}
class Derived: Base {
class override func factory() -> Derived { /*...*/ }
}
例如:struct Foo
例如: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
}
例如:
func forceCast(_ value: Any?, to type: U.Type) -> U {
return value as! U
}
print(forceCast(value, to: Any.self))
// Prints "Optional(42)"
// (Prior to Swift 5, this would print "42".)
// Prints "Optional(42)"
协议现在可以将符合类型限定为给定类的子类。支持两种等效形式:
protocol MyView where Self: UIView { /.../ Swift 4.2 接受了第二种形式,但还没有完全实现,在编译时或运行时偶尔会发生崩溃。
* 在 Swift 5 模式下,当在自己的 didSet 或 willSet observer 中设置属性时,observer 现在只在 self 上设置属性(不管是隐式的还是显式的)时才会避免被递归调用。
例如:
var children = [Node]()
var depth: Int = 0 {
didSet {
if depth // 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
}
}
}
* 如果你使用 #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