记录 Swift 5.3 的新特性!
1. String 添加了一个初始化方法
可以直接从 UTF8 array, 初始化一个 String
let validUTF8: [UInt8] = [67, 97, 102, 128 + (128 - 61), 128 + (128 - 87), 0]
let s = String(unsafeUninitializedCapacity: validUTF8.count,
initializingUTF8With: { ptr in
ptr.initialize(from: validUTF8)
return validUTF8.count
})
print(s)
// print
Café
2. enum
添加 Comparable
默认实现
Swift 5.3 之后, enum 的大小是根据你定义时候的顺序决定的, 从大到小一次定义的,
就是最开始定义的是最大的, 如果莫哥 case 中含有参数, 那么这个参数必须也实现了Comparable
, 根据正常的理解一样, 不同参数还有依次的排序, 如下代码, 一看就懂了:
enum Membership: Comparable {
case premium(Int)
case preferred
case general
}
var members: [Membership] = [.preferred, .premium(1), .general, .premium(0), .premium(-2)]
print(members.sorted(by: { $0 > $1 }))
// print
[__lldb_expr_17.Membership.general,
__lldb_expr_17.Membership.preferred, __lldb_expr_17.Membership.premium(1), __lldb_expr_17.Membership.premium(0), __lldb_expr_17.Membership.premium(-2)]
3. getter
不会自动在 didSet 中调用, 除非在 didSet 中访问了 oldValue
对比下面两段代码, 第一段在 Swift5.3 之前, 会挂掉, Swift5.3之后不会
Swift5.3 在第二段这种情况才会挂掉
代码1:
@propertyWrapper
struct Delayed<Value> {
var wrappedValue: Value {
get {
guard let value = value else {
preconditionFailure("Property \(String(describing: self)) has not been set yet")
}
return value
}
set {
guard value == nil else {
preconditionFailure("Property \(String(describing: self)) has already been set")
}
value = newValue
}
}
var value: Value?
}
class Foo {
@Delayed var bar: Int {
didSet { print("didSet called") }
}
}
let foo = Foo()
foo.bar = 1
// print
// Swift 5.3
didSet called
// 小于 Swift 5.3
Fatal error: Property Delayed<Int>(value: nil) has not been set yet: file __lldb_expr_13/MyPlayground.playground, line 68
代码2:
@propertyWrapper
struct Delayed<Value> {
var wrappedValue: Value {
get {
guard let value = value else {
preconditionFailure("Property \(String(describing: self)) has not been set yet")
}
return value
}
set {
guard value == nil else {
preconditionFailure("Property \(String(describing: self)) has already been set")
}
value = newValue
}
}
var value: Value?
}
class Foo {
@Delayed var bar: Int {
didSet { print("didSet called \(oldValue)") }
}
}
let foo = Foo()
foo.bar = 1
// print
Fatal error: Property Delayed<Int>(value: nil) has not been set yet: file __lldb_expr_13/MyPlayground.playground, line 68
4. 在闭包中的隐式 self
的使用
- 可以把
[self]
添加到闭包的捕获列表里面, 或者闭包的参数是 self 的一个方法
class Test {
var x = 0
func execute(_ work: @escaping () -> Void) {
work()
}
func method() {
execute(inc)
execute { [self] in
inc()
}
}
func inc() {
x += 1
}
}
- 如果
self
是值类型, 不需要添加到捕获列表里面, self 就可以隐式使用
struct Test {
var x = 0
func execute(_ work: @escaping () -> Void) {
work()
}
func method() {
execute {
run()
}
}
func run() {
print("run")
}
}
var t = Test()
t.method()
// print
run
- 题外话, 值类型如何在逃逸闭包里面修改自己的属性, 智障
struct Test {
var x = 0
func execute(_ work: @escaping () -> Void) {
work()
}
mutating func method() {
var result = self
execute {
result.x += 1
print(result.x)
}
self = result
}
}
var t = Test()
t.method()
5. catch
可以分类了
enum TaskError: Error {
case someRecoverableError
case someFailure(String)
case anotherFailure(String)
}
func performTask() throws {
throw TaskError.someFailure("???????")
}
do {
try performTask()
} catch TaskError.someRecoverableError { // OK
print("someRecoverableError")
} catch TaskError.someFailure(let msg),
TaskError.anotherFailure(let msg) { // Also Allowed
print(msg)
}
6. 添加了 Float16
类型
7. 尾闭包的样式修改
经历了很多样式, 最后现在为:
func resolve(
id: Int,
action: (Int) -> Void,
completion: (() -> Void)? = nil,
onError: ((Error) -> Void)? = nil
) {
}
resolve(id: 0) { _ in
}
resolve(id: 0) { _ in
} onError: { _ in
}
8. enum 实现 protocol
具体的实现参考下面代码, 目前无参数的方法, 无法通过 case 来实现, 其他的需要对应格式实现, 如下:
protocol Foo {
static var zero: FooEnum { get }
static var one: Self { get }
static func two(arg: Int) -> FooEnum
static func three(_ arg: Int) -> Self
static func four(_ arg: String) -> Self
static var five: Self { get }
static func six(_: Int) -> Self
static func seven(_ arg: Int) -> Self
static func eight() -> Self
}
enum FooEnum: Foo {
static func eight() -> FooEnum {
return .eight
}
case zero // okay
case one // okay
case two(arg: Int) // okay
case three(_ arg: Int) // okay
// case four(arg: String) // not a match
case four(_ arg: String) // okay
// case five(arg: Int) // not a match
case five // okay
case six(Int) // okay
case seven(Int) // okay
case eight // not a match
}
9. @main
入口
@main
@NSApplicationMain与@main 在 Swift5.3 之后相同
其他还有为一些脚本使用
- 桌面新建一个叫 Command.swift 的 swift 文件
@main
struct Command {
static func main() {
print("run")
}
}
- cmd 在桌面文件夹下
swiftc -parse-as-library Command.swift
得到可执行文件, 双击, 得到
/Users/xxxx/Desktop/Command ; exit;
run
- 如果报错, 建议新建一个文件夹, 在这个文件夹下操作, 因为 build 的时候, 会查找当前文件夹和子文件夹, 如果存在多个 @main 就会报错
- 使用 swift package
swift package init --type executable
会初始化一个 SPM, 其中包含一个 main.swift
swift build
swift run
// print
Hello, world!
如果使用xxx.swift替换 main.swift, 并且在 xxx.swift 中添加 @main,
swift build 便不好用
所以说 SPM, 并没有应用 @main
的这种方式来构建
@main
is no longer usable due to misdetection of top level code
10. 新增#filePath #fileID
隐私问题, #file 不注重隐私, 所以替换这两个
目标是, 如下代码:
print(#file)
print(#filePath)
fatalError("Something bad happened!")
MagicFile/0274-magic-file.swift
/Users/brent/Desktop/0274-magic-file.swift
Fatal error: Something bad happened!: file MagicFile/0274-magic-file.swift, line 3
Swift 5.3 目前是, 如下代码:
print(#file)
print(#fileID)
print(#filePath)
fatalError("Something bad happened!")
/Users/brent/Desktop/0274-magic-file.swift
MagicFile/0274-magic-file.swift
/Users/brent/Desktop/0274-magic-file.swift
Fatal error: Something bad happened!: file MagicFile/0274-magic-file.swift, line 3
Swift 5.3 assert
, precondition
, fatalError
输出的都是 #fileID
, 而不是#file
.
Swift 5.3 目前推荐使用 #fileID
, 1 节省空间, 2 安全, 暴露更少的细节.
Ease the transition to concise magic file strings
网友评论