Swift 5 新特性預覽(最低支持Xcode 10.2 beta版)

萬衆期待的 Swift 5 終於來了,蘋果爸爸答應的 ABI 穩定也終於來了。ios

小集新小夥伴 @NotFound-- 花時間將文檔翻譯出來,供你們參考。翻譯不當之處,請及時留言指出,咱們會持續更新。git

App 瘦身

新特性

Swift 應用程序再也不包含用於 Swift 標準庫的動態連接庫和用於運行 iOS 12.2watchOS 5.2tvOS 12.2 的設備的構建變體中的 Swift SDK overlays。所以,當爲 TestFlight 進行測試部署時,或者在爲本地開發分發瘦身應用的 archive 包時,Swift 應用程序能夠更小。github

要對比 iOS 12.2 和 iOS 12.1(或更早版本) 瘦身後 App 的文件大小差別,能夠設置 App 的 deployment targetiOS 12.1 或更早版本,設置 scheme setGeneric iOS Device 並生成一個 App 的歸檔。在構建完成後,在 Archives organizer 選擇中 Distribute App,而後選擇 Development distribution。確保在 App Thinning 下拉菜單中選擇一個特定設備,如 iPhone XS。當分發完成後,在新建立的文件夾下打開 App Thinning Size Report。iOS 12.2 系統的變體將小於 iOS 12.1 及更早的系統的變體。確切的大小差別取決於您的 App 使用的系統框架的數量。express

關於 App 瘦身更多的信息,能夠查看 Xcode Help 中的 What is app thinning?有關應用程序文件大小的信息,請參考 App Store Connect Help 中的 View builds and file sizesswift

Swift 語言

新特性

  • @dynamicCallable 容許您使用一個簡單的語法糖像調用函數同樣來調用命名類型。主要的應用場景是動態語言互操做。(SE-0216)api

    例如:數組

@dynamicCallable struct ToyCallable {
    func dynamicCall(withArguments:[Int]){}
    func dynamicCall(withKeywordArguments:KeyValuePairs <String,Int>){}
}

let x = ToyCallable()

x(1,2,3)
// 等價於`x.dynamicallyCall(withArguments:[1,2,3])`

x(label: 1, 2)
// 等價於`x.dynamicallyCall(withKeywordArguments: ["label": 1, "": 2])`
複製代碼
  • Key path 如今支持特性(identity) keypath (\.self),這是一個引用自身完整輸入值的 WritableKeyPath。(SE-0227)
let id = \Int.self
var x = 2
print(x[keyPath: id]) // Prints "2"
x[keyPath: id] = 3
print(x[keyPath: id]) // Prints "3"
複製代碼
  • 在 Swift 5 以前,您能夠編寫一個帶有可變參數的枚舉 case:
enum X {
    case foo(bar: Int...) 
}
func baz() -> X {
    return .foo(bar: 0, 1, 2, 3) 
} 
複製代碼

以前不是特地要支持這個特性,並且如今這樣寫會報錯了。xcode

取而代之的是,讓枚舉的 case 攜帶一個數組,並顯式傳遞一個數組安全

enum X {
    case foo(bar: [Int]) 
} 

func baz() -> X {
    return .foo(bar: [0, 1, 2, 3]) 
} 
複製代碼
  • 在 Swift 5 中,帶有一個可選類型的表達式的 try? 將會展生平成的可選項,而不是返回嵌套的可選項。(SE-0230)bash

  • 若是類型T 符合Initialized with Literals中的其中一個協議(如 ExpressibleByIntegerLiteral),且 literal 是一個字面量表達示時,則 T(literal) 會使用相應的協議建立一個類型 T 的字面量,而不是使用一個協議的默認字面量類型的值來調用 Tinitializer

    如,相似於 UInt64(0xffff_ffff_ffff_ffff) 這樣的表達式如今是有效的,則以前會因爲整型字面量的默認類型是 Int,而致使溢出。(SE-0213)

  • 提升了字符串插值操做的性能、清晰性和效率。(SE-0228)

    舊的 _ExpressibleByStringInterpolation 協議被刪除;若是您有使用此協議的代碼,則須要作相應更新。您可使用 #if 條件判斷來區分 Swift 4.2Swift 5 的代碼。例如:

#if compiler(<5)
extension MyType: _ExpressibleByStringInterpolation { /*...*/ }
#else
extension MyType: ExpressibleByStringInterpolation { /*...*/ }
#endif 
複製代碼

Swift 標準庫

新功能

  • DictionaryLiteral 類型重命名爲 KeyValuePairs。(SE-0214)

  • 橋接到 Objective-C 代碼的 Swift 字符串如今能夠在適當的時候從 CFStringGetCStringPtr返回一個 non-nil 值,同時從 -UTF8String 返回的指針與字符串的生命週期相關聯,而不是最相近的那個 autorelease pool。若是程序正確,那應該沒有任何問題,而且會發現性能顯著提升。可是,這也可能會讓以前一些未經測試的代碼運行,從而暴露一些潛在的問題;例如,若是有一個對 non-nil 值的判斷,而相應分支在 Swift 5 以前卻從未被執行過。(26236614)

  • Sequence 協議再也不具備 SubSequence 關聯類型。先前返回 SubSequenceSequence 方法如今會返回具體類型。例如,suffix(_:)如今會返回一個 Array。(47323459)

    使用 SubSequenceSequence 擴展應該修改成相似地使用具體類型,或者修改成 Collection 的擴展,在 CollectionSubSequence 仍然可用。(45761817)

例如:

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.UTF16View 相比,這會提升相關聯的 String.UTF8View 的性能。從新對全部代碼進行評審以提升性能,尤爲是使用了 String.UTF16View 的代碼。

Swift 包管理器

新功能

  • 如今,在使用 Swift 5 軟件包管理器時,Targets 能夠聲明一些經常使用的針對特定目標的 build settings 設置。新設置也能夠基於平臺和構建配置進行條件化處理。包含的構建設置支持 SwiftC 語言定義,C 語言頭文件搜索路徑,連接庫和連接框架。(SE-0238)(23270646)

  • 在使用 Swift 5 軟件包管理器時,package 如今能夠自定義 Apple 平臺的最低 deployment target。而若是 package A 依賴於 package B,但 package B 指定的最小 deployment target 高於 package A 的最小 deployment target,則構建 package A 時會拋出錯誤。(SE-0236)(28253354)

  • 新的依賴鏡像功能容許頂層包覆蓋依賴 URL。(SE-0219)(42511642)

    使用如下命令設置鏡像:

$ swift package config set-mirror \
--package-url <original URL> --mirror-url <mirror URL>
複製代碼
  • swift 測試命令可使用標誌 --enable-code-coverage,來生成標準格式的代碼覆蓋率數據,以便其它代碼覆蓋工具使用。生成的代碼覆蓋率數據存儲在 <build-dir>/<configuration>/codecov 目錄中。

  • Swift 5 再也不支持 Swift 3 版本的軟件包管理器。仍然在使用 Swift 3 Package.swift 工具版本(tool-version)上的軟件包應該更新到新的工具版本上。

  • 對體積較大的包進行包管理器操做如今明顯更快了。

  • Swift 包管理器有一個新的 --disable-automatic-resolution 標誌項,當 Package.resolved 條目再也不與 Package.swift 清單文件中指定的依賴項版本兼容時,該標誌項強制包解析失敗。此功能對於持續集成系統很是有用,能夠檢查包的 Package.resolved 是否已過時。

  • swift run 命令有一個新的 --repl 選項,它會啓動 Swift REPL,支持導入包的庫目標。這使您能夠輕鬆地從包目標中試用 API,而無需構建調用該 API 的可執行文件。

  • 有關使用 Swift 包管理器的更多信息,請訪問 swift.org 上的 Using the Package Manager

Swift 編譯器

新特性

  • 如今,在優化(-O-Osize)構建中,默認狀況下在運行時強制執行獨佔內存訪問。違反排他性的程序將在運行時拋出帶有「重疊訪問」診斷消息錯誤。您可使用命令行標誌禁用此命令:-enforce-exclusivity = unchecked,但這樣作可能會致使未定義的行爲。運行時違反排他性一般是因爲同時訪問類屬性,全局變量(包括頂層代碼中的變量)或經過 eacaping 閉包捕獲的變量。(SR-7139

  • Swift 3 運行模式已被刪除。-swift-version 標誌支持的值爲 44.25

  • 在 Swift 5 中,在 switch 語句中使用 Objective-C 中聲明的或來自系統框架的枚舉時,必須處理未知的 case,這些 case 可能未來會添加,也多是在 Objective-C實現文件中私下定義。形式上,Objective-C 容許在枚舉中存儲任何值,只要它匹配底層類型便可。這些未知的 case 可使用新的 @unknown default case 來處理,固然若是 switch 中省略了任何已知的 case,編譯器仍然會給出警告。它們也可使用普通的 default case 來處理。

    若是您已在 Objective-C 中定義了本身的枚舉,而且不須要客戶端來處理 unknown case,則可使用 NS_CLOSED_ENUM 宏而不是 NS_ENUM。Swift 編譯器識別出這一點,而且不需求 switch 語句必須帶有 default case

    Swift 44.2 模式下,您仍然可使用 @unknown default。若是省略 @unknown default,而又傳遞了一個未知的值,則程序在運行時拋出異常,這與 Xcode 10.1 中的 Swift 4.2 上的行爲是一致的。(SE-0192)(39367045)

  • 如今在 SourceKit 生成的 Swift 模塊接口中會打印默認參數,而不只僅是使用佔位符。

  • unownedunowned(unsafe) 類型的變量如今支持可選類型。

已知的問題

  • 若是引用了 UIAccessibility 結構的任何成員,則 Swift 編譯器會在 「Merge swiftmodule」 構建步驟中崩潰。構建日誌包含一條消息:
Cross-reference to module 'UIKit'
... UIAccessibility
... in an extension in module 'UIKit' ... GuidedAccessError 複製代碼

包含 NS_ERROR_ENUM 枚舉的其餘類型也可能出現此問題,但 UIAccessibility 是最多見的。(47152185)

解決方法:在 targetBuild Setting -> Swift Compiler -> Code Generation 下,設置 Compilation Mode 的值爲 Whole Module。這是大多數 Release 配置的默認設置。

  • 爲了減少 Swift 元數據的大小,Swift 中定義的 convenience initializers 若是調用了 Objective-C 中定義的一個 designated initializer,那隻會提早分配一個對象。在大多數狀況下,這對您的程序沒有影響,但若是從 Objective-C 調用 convenience initializers,那麼 +alloc 分配的初始內存會被釋放,而不會調用任何 initializer。對於不但願發生任何類型的對象替換的調用者來講,這多是有問題的。其中一個例子是 initWithCoder: :若是 NSKeyedUnarchiver 調用 Swift 實現 init(coder:) 而且存檔對象存在循環時,則 NSKeyedUnarchiver 的實現可能會出錯。

    在未來的版本中,編譯器將保證一個 convenience initializer 永遠不會丟棄它所調用的對象,只要它經過 self.init 委託給它的初始化程序也暴露給 Objective-C,或者是它在 Objective-C 中定義了,或者是使用 @objc 標記的,或者是重寫了一個暴露給 Objective-Cinitializer,或者是它知足 @objc 協議的要求。(46823518)

  • 若是一個 keypath 字面量引用了 Objective-C 中定義的屬性,或者是在 Swift 中使用 @objcdynamic 修飾符定義的屬性,則編譯可能會失敗,而且報 「unsupported relocation of local symbol 'L_selector'」 錯誤,或者 key path 字面量沒法在運行時生成正確的哈希值或處理相等比較。

    解決方法:您能夠定義一個不是 @objc 修飾的包裝屬性,來引用這個 key path。獲得的 key path 與引用原始 Objective-C 屬性的 key path 不相等,但使用包裝屬性效果是相同的。

  • 某些項目可能會遇到之前版本的編譯時迴歸。

  • Swift 命令行項目在啓動時因拋出 「dyld:Library not loaded」 錯誤而崩潰。

    解決方法:添加自定義的構建設置 SWIFT_FORCE_STATIC_LINK_STDLIB=YES

已解決的問題

  • 擴展綁定如今支持嵌套類型的擴展,這些嵌套類型自己是在擴展內定義的。以前可能會由於一些聲明順序而失敗,併產生 「未聲明類型」 錯誤。(SR-631)

  • 在 Swift 5 中,返回 Self 的類方法不能再被使用返回非 final 的具體類類型的方法來覆蓋。此類代碼不是類型安全的,須要更新。(SR-695)

    例如:

class Base { 
    class func factory() -> Self { /*...*/ }
} 

class Derived: Base {
    class override func factory() -> Derived { /*...*/ } 
} 
複製代碼
  • 在 Swift 5 模式下,如今會明確禁止聲明與嵌套類型同名的靜態屬性。之前,能夠在泛型類型的擴展中執行這樣的聲明。(SR-7251)

    例如:

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 相同的行爲。(SR-5719

    例如:

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 中,當將可選值轉換爲泛型佔位符類型時,編譯器在解包值時會更加謹慎。這種轉換的結果如今更接近於非泛型上下文中的結果。(SR-4248)

    例如:

func forceCast<U>(_ value: Any?, to type: U.Type) -> U {
    return value as! U 
} 

let value: Any? = 42
print(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 接受了第二種形式,但沒有徹底實現,有時可能在編譯時或運行時崩潰。(SR-5581)

  • 在 Swift 5 中,當在屬性自身的 didSetwillSet 中設置屬性自己時,會避免遞歸調用(不管是隱式或顯式地設置自身的屬性)。(SR-419)

例如:

class Node {
    var children = [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
            } 
        }
    }
}
複製代碼
  • Xcode中 的 diagnostics#sourceLocation 進行了支持。也就是說,若是您使用#sourceLocation 將生成的文件中的行映射回源代碼時,diagnostics 會顯示在原始源文件中的行數而不是生成的文件中的。

  • 使用泛型類型別名做爲參數或 @objc 方法的返回類型,再也不致使生成無效的 Objective-C header。(SR-8697)

關注咱們

歡迎關注咱們的公衆號:iOS-Tips,也歡迎加入咱們的羣組討論問題。能夠公衆號留言 iosflutter 等關鍵詞獲取入羣方式。

相關文章
相關標籤/搜索