Swift 5.3

作者: 幸运者_Lucky | 来源:发表于2020-09-23 18:35 被阅读0次

    记录 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 的使用

    1. 可以把 [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
        }
    }
    
    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
    
    1. 题外话, 值类型如何在逃逸闭包里面修改自己的属性, 智障
    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 类型

    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 之后相同
    其他还有为一些脚本使用

    1. 桌面新建一个叫 Command.swift 的 swift 文件
    @main
    struct Command {
        static func main() {
            print("run")
        }
    }
    
    
    1. cmd 在桌面文件夹下
    swiftc -parse-as-library Command.swift
    

    得到可执行文件, 双击, 得到

    /Users/xxxx/Desktop/Command ; exit;
    run
    
    1. 如果报错, 建议新建一个文件夹, 在这个文件夹下操作, 因为 build 的时候, 会查找当前文件夹和子文件夹, 如果存在多个 @main 就会报错
    2. 使用 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

    相关文章

      网友评论

          本文标题:Swift 5.3

          本文链接:https://www.haomeiwen.com/subject/vxhwyktx.html