Swift 5.1 新特性

在 7 月 29 日的發(fā)布的 Xcode 11 beta 5 中鳄抒,包含了 Swift 5.1闯捎。如果想要體驗(yàn)這些新特性,需要至少安裝好這個(gè)版本的 Xcode许溅。本文內(nèi)容主要參考 Raywenderlich這篇文章 編寫瓤鼻,如果想要查看原文,請(qǐng)點(diǎn)擊鏈接查看贤重。

Swift 5.1 在 5.0 引入的 ABI 穩(wěn)定性基礎(chǔ)上增加了模塊穩(wěn)定性茬祷。雖然 ABI 穩(wěn)定性在運(yùn)行時(shí)考慮到應(yīng)用程序的兼容性,但模塊穩(wěn)定性在編譯時(shí)支持庫的兼容性并蝗。這意味著你可以在任何編譯器版本中使用第三方框架祭犯,而不只是構(gòu)建它的那個(gè)版本。

下面我們一起看一下有哪些改進(jìn)滚停。

模糊的結(jié)果類型 (Opaque Result Types)

在開發(fā)的時(shí)候沃粗,有時(shí)候可能會(huì)使用 protocol 作為返回值類型。下面來看一個(gè)例子:

protocol BlogPost {
    var title: String { get }
    var author: String { get }
}

struct Tutorial: BlogPost {
    let title: String
    let author: String
}

func createBlogPost(title: String, author: String) -> BlogPost {
    guard !title.isEmpty && !author.isEmpty else {
        fatalError("No title and/or author assigned!")
    }
    return Tutorial(title: title, author: author)
}

let swift4Tutorial = createBlogPost(title: "What's new in Swift 4.2?",
                                    author: "Cosmin Pup?z?")
let swift5Tutorial = createBlogPost(title: "What's new in Swift 5?",
                                    author: "Cosmin Pup?z?")

上面代碼:1)首先定義 BlogPost 協(xié)議铐刘;2)定義 Tutorial 并實(shí)現(xiàn) BlogPost 協(xié)議;3)定義 createBlogPost() 方法用于創(chuàng)建 BlogPost影晓;4)用 createBlogPost() 創(chuàng)建 swift4Tutorialswift5Tutorial 兩個(gè)實(shí)例镰吵。

下面我們想比較 swift4Tutorialswift5Tutorial 是否相等:

// 錯(cuò)誤:Binary operator '==' cannot be applied to two 'BlogPost' operands
let isSameTutorial = (swift4Tutorial == swift5Tutorial)

因?yàn)?BlogPost 還沒有實(shí)現(xiàn) Equatable檩禾,所以會(huì)出錯(cuò)。下面讓 BlogPost 繼承自 Equatable

protocol BlogPost: Equatable {
    var title: String { get }
    var author: String { get }
}

這時(shí)另一個(gè)錯(cuò)誤出現(xiàn)在 createBlogPost() 方法:

// Protocol 'BlogPost' can only be used as a generic constraint because it has Self or associated type requirements
func createBlogPost(title: String, author: String) -> BlogPost {
    guard !title.isEmpty && !author.isEmpty else {
        fatalError("No title and/or author assigned!")
    }
    return Tutorial(title: title, author: author)
}

這個(gè)錯(cuò)誤的意思是 BlogPost 只能用來作為泛型約束疤祭,因?yàn)樗?Self 或者有關(guān)聯(lián)類型要求盼产。查看 Equatable的定義勺馆,確實(shí)是有 Self

public protocol Equatable {
    static func == (lhs: Self, rhs: Self) -> Bool
}

具有關(guān)聯(lián)類型的協(xié)議不是類型戏售,即使他們看起來是類型。而它們更像是類型占位符草穆,這個(gè)類型可以是任何實(shí)現(xiàn)了我這個(gè)協(xié)議的類型灌灾。

在 Swift 5.1 中,我們就可以在返回值類型前面加上 some 來解決這個(gè)問題悲柱。把 createBlogPost() 方法改為:

func createBlogPost(title: String, author: String) -> some BlogPost {
    guard !title.isEmpty && !author.isEmpty else {
        fatalError("No title and/or author assigned!")
    }
    return Tutorial(title: title, author: author)
}

some 的作用就是告訴編譯器我這個(gè)方法的返回值可以是實(shí)現(xiàn)了 BlogPost 的任何類型锋喜。

修改完成之后,我們直接用 == 比較 swift4Tutorialswift5Tutorial 就不會(huì)報(bào)錯(cuò)了豌鸡。

在 SwiftUI 中嘿般,就是使用了 some

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

隱式返回

在 Swift 5.1 中,如果方法體只有一行語句涯冠,則可以省略 return

func myName() -> String {
    "Lebron"
}

屬性包裝器

在 Swift 5.1 之前炉奴,使用計(jì)算屬性時(shí),可能出現(xiàn)類似下面的代碼:

var settings = ["swift": true, "latestVersion": true]

struct Settings {
    var isSwift: Bool {
        get {
            return settings["swift"] ?? false
        }
        set {
            settings["swift"] = newValue
        }
    }
    
    var isLatestVersion: Bool {
        get {
            return settings["latestVersion"] ?? false
        }
        set {
            settings["latestVersion"] = newValue
        }
    }
}

var newSettings = Settings()
newSettings.isSwift
newSettings.isLatestVersion
newSettings.isSwift = false
newSettings.isLatestVersion = false

上面的代碼中蛇更,如果有更多的計(jì)算屬性瞻赶,那么就要寫跟多的重復(fù)代碼。為了解決這個(gè)問題械荷,Swift 5.1 引入了屬性包裝器共耍,可以把上面的代碼簡(jiǎn)寫為:

var settings = ["swift": true, "latestVersion": true]

@propertyWrapper
struct SettingsWrapper {
  let key: String
  let defaultValue: Bool

  var wrappedValue: Bool {
    get {
      settings[key] ?? defaultValue
    }
    set {
      settings[key] = newValue
    }
  }
}

struct Settings {
  @SettingsWrapper(key: "swift", defaultValue: false)         var isSwift: Bool
  @SettingsWrapper(key: "latestVersion", defaultValue: false) var isLatestVersion: Bool
}
  • @propertyWrapperSettingsWrapper 標(biāo)記為屬性包裝器。作為一個(gè)屬性包裝器吨瞎,必須有一個(gè)名為wrappedValue 的屬性痹兜。
  • 使用 @SettingsWrapper 標(biāo)記 Settings中對(duì)應(yīng)的屬性。

在 struct 中定義屬性的默認(rèn)值

在 Swift 5.1 前颤诀,如果想要給 struct 的屬性定義默認(rèn)值字旭,必須這么寫:

struct Author {
    let name: String
    var tutorialCount: Int
    
    init(name: String, tutorialCount: Int = 0) {
        self.name = name
        self.tutorialCount = tutorialCount
    }
}

let author = Author(name: "George")

而在 Swift 5.1 以后,可以直接像 class 那樣給屬性定義默認(rèn)值:

struct Author {
    let name: String
    var tutorialCount = 0
}

使用 Self 調(diào)用靜態(tài)成員

在 Swift 5.1 以前崖叫,需要使用 類名.靜態(tài)成員來調(diào)用靜態(tài)成員:

struct Editor {
    static func reviewGuidelines() {
        print("Review editing guidelines.")
    }
    
    func edit() {
        Editor.reviewGuidelines()
        print("Ready for editing!")
    }
}

而在 Swift 5.1 中遗淳,可以直接用 Self.靜態(tài)成員

struct Editor {
    static func reviewGuidelines() {
        print("Review editing guidelines.")
    }
    
    func edit() {
        Self.reviewGuidelines()
        print("Ready for editing!")
    }
}

創(chuàng)建未初始化的數(shù)組

Swift 5.1 給 Array 添加了一個(gè)新的初始化方法:init(unsafeUninitializedCapacity:initializingWith:)

let randomSwitches = Array<String>(unsafeUninitializedCapacity: 5) { buffer, count in
    for i in 0..<5 {
        buffer[i] = Bool.random() ? "on" : "off"
    }
    // 必須給 `count` 賦值,否則 `randomSwitches` 會(huì)變成空數(shù)組
    count = 5
}

staticclass 下標(biāo)

在 Swift 5.1 中可以定義 staticclass 下標(biāo):

@dynamicMemberLookup
class File {
    let name: String
    
    init(name: String) {
        self.name = name
    }
    
    // 定義 static 下標(biāo)
    static subscript(key: String) -> String {
        switch key {
        case "path":
            return "custom path"
        default:
            return "default path"
        }
    }
    
    // 使用 Dynamic Member Lookup 重寫上面的下標(biāo)
    class subscript(dynamicMember key: String) -> String {
        switch key {
        case "path":
            return "custom path"
        default:
            return "default path"
        }
    }
}

File["path"] // "custom path"
File["PATH"] // "default path"
File.path    // "custom path"
File.PATH    // "default path"

@dynamicMemberLookup 標(biāo)記 File 是為了可以使用點(diǎn)語法來訪問自定義的下標(biāo)心傀。

Keypath 支持動(dòng)態(tài)成員查找

struct Point {
    let x, y: Int
}

@dynamicMemberLookup
struct Circle<T> {
    let center: T
    let radius: Int
    
    // 定義泛型下標(biāo)屈暗,可以用 keypath 來訪問 `center` 的屬性
    subscript<U>(dynamicMember keyPath: KeyPath<T, U>) -> U {
        center[keyPath: keyPath]
    }
}

let center = Point(x: 1, y: 2)
let circle = Circle(center: center, radius: 1)
circle.x // 1
circle.y // 2

Tuple 支持 Keypath

struct Instrument {
    let brand: String
    let year: Int
    let details: (type: String, pitch: String)
}

let instrument = Instrument(
    brand: "Roland",
    year: 2019,
    details: (type: "acoustic", pitch: "C")
)
// 使用 keypath 訪問 tuple
let type = instrument[keyPath: \Instrument.details.type]
let pitch = instrument[keyPath: \Instrument.details.pitch]

weakunowned 屬性自動(dòng)實(shí)現(xiàn) EquatableHashable

class Key {
    let note: String
    
    init(note: String) {
        self.note = note
    }
}

extension Key: Hashable {
    static func == (lhs: Key, rhs: Key) -> Bool {
        lhs.note == rhs.note
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(note)
    }
}

class Chord {
    let note: String
    
    init(note: String) {
        self.note = note
    }
}

extension Chord: Hashable {
    static func == (lhs: Chord, rhs: Chord) -> Bool {
        lhs.note == rhs.note
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(note)
    }
}

struct Tune: Hashable {
    unowned let key: Key
    weak var chord: Chord?
}

在 Swift 5.1 以前,Tune 的定義里面會(huì)報(bào)錯(cuò):沒有實(shí)現(xiàn) EquatableHashable;在 Swift 5.1 則已經(jīng)自動(dòng)實(shí)現(xiàn)养叛。

不明確的枚舉 case

如果有不明確的枚舉 case种呐,在 Swift 5.1 中會(huì)產(chǎn)生警告??。

enum TutorialStyle {
  case cookbook, stepByStep, none
}

// 會(huì)產(chǎn)生警告
let style: TutorialStyle? = .none

因?yàn)?styleOptional 類型弃甥,編譯器不知道 .noneOptional.none 還是 TutorialStyle.none 爽室,所有要寫具體一點(diǎn),例如:let style: TutorialStyle? = TutorialStyle.none

匹配可選類型的枚舉

在 Swift 5.1 以前淆攻,switch 語句中匹配可選類型的枚舉時(shí)阔墩,case 后面需要加問號(hào):

enum TutorialStatus {
    case written, edited, published
}

let status: TutorialStatus? = .published

switch status {
    case .written?:
        print("Ready for editing!")
    case .edited?:
        print("Ready to publish!")
    case .published?:
        print("Live!")
    case .none:
        break
}

而在 Swift 5.1 中,可以把問號(hào)去掉:

switch status {
    case .written:
        print("Ready for editing!")
    case .edited:
        print("Ready to publish!")
    case .published:
        print("Live!")
    case .none:
        break
}

Tuple 類型的轉(zhuǎn)換

let temperatures: (Int, Int) = (25, 30)
let convertedTemperatures: (Int?, Any) = temperatures

在 Swift 5.1 以前瓶珊,(Int, Int) 是不能轉(zhuǎn)換成 (Int?, Any) 的啸箫,而在 Swift 5.1 可以。

Any 和泛型參數(shù)的方法重載

func showInfo(_: Any) -> String {
  return "Any value"
}

func showInfo<T>(_: T) -> String {
  return "Generic value"
}

showInfo("Swift")

在 Swift 5.1 以前艰毒,showInfo("Swift") 返回的是 Any value筐高;而在 Swift 5.1 中,返回的是 Generic value丑瞧。也就是說在 Swift 5.1 以前柑土,Any 參數(shù)類型的方法優(yōu)先;而在 Swift 5.1 中绊汹,泛型參數(shù)的方法優(yōu)先稽屏。

autoclosure 參數(shù)可以定義別名

在 Swift 5.1 中,@autoclosure 標(biāo)記的 closure 參數(shù)可以使用別名:

struct Closure<T> {
    typealias ClosureType = () -> T
    
    func apply(closure:  @autoclosure ClosureType) {
        closure()
    }
}

在 Swift 5.1 以前西乖,只能這樣寫:

struct Closure<T> {
    func apply(closure: @autoclosure () -> T) {
        closure()
    }
}

在 Objective-C 方法中返回 self

在 Swift 5.1 以前狐榔,被 @objc 標(biāo)記的方法中返回 self,必須繼承自 NSObject

class Clone: NSObject {
    @objc func clone() -> Self {
        return self
    }
}

在 Swift 5.1 中获雕,則無需集成 NSObject

class Clone {
  @objc func clone() -> Self {
    self
  }
}

想及時(shí)看到我的新文章的薄腻,可以關(guān)注我。同時(shí)也歡迎加入我管理的Swift開發(fā)群:536353151届案。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庵楷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子楣颠,更是在濱河造成了極大的恐慌尽纽,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件童漩,死亡現(xiàn)場(chǎng)離奇詭異弄贿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)矫膨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門差凹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來期奔,“玉大人,你說我怎么就攤上這事危尿∧芮欤” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵脚线,是天一觀的道長。 經(jīng)常有香客問我弥搞,道長邮绿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任攀例,我火速辦了婚禮船逮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘粤铭。我一直安慰自己挖胃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布梆惯。 她就那樣靜靜地躺著酱鸭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪垛吗。 梳的紋絲不亂的頭發(fā)上凹髓,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音怯屉,去河邊找鬼蔚舀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛锨络,可吹牛的內(nèi)容都是我干的赌躺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼羡儿,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼礼患!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起失受,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤讶泰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拂到,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痪署,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年兄旬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狼犯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片余寥。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖悯森,靈堂內(nèi)的尸體忽然破棺而出宋舷,到底是詐尸還是另有隱情,我是刑警寧澤瓢姻,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布祝蝠,位于F島的核電站,受9級(jí)特大地震影響幻碱,放射性物質(zhì)發(fā)生泄漏绎狭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一褥傍、第九天 我趴在偏房一處隱蔽的房頂上張望儡嘶。 院中可真熱鬧,春花似錦恍风、人聲如沸蹦狂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凯楔。三九已至,卻和暖如春锦募,著一層夾襖步出監(jiān)牢的瞬間啼辣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國打工御滩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸥拧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓削解,卻偏偏與公主長得像富弦,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子氛驮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容