欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Swift中非可選的可選值類型處理方法詳解

 更新時(shí)間:2017年11月15日 09:11:11   作者:johnsundell  
Optional是Objective-C沒有的數(shù)據(jù)類型,是蘋果引入到Swift語言中的全新類型,它的特點(diǎn)就和它的名字一樣:可以有值,也可以沒有值,當(dāng)它沒有值時(shí),就是nil。下面這篇文章主要給大家介紹了關(guān)于Swift中非可選的可選值類型處理方法的相關(guān)資料,需要的朋友可以參考下。

前言

在我們使用objective-c表示字符串信息的時(shí)候,可以用下面方法書寫。

NSString *str = @"秋恨雪"; 
str = nil; 

因?yàn)閛bjective-c是弱類型語言,所以這里的str既可以是具體的字符串也可以是nil。但到了Swift中就不可以了,因?yàn)镾wift是類型安全的語言,一個(gè)String類型的變量不可能既能是具體的字符串,又可以為nil(更嚴(yán)格的說String類型的內(nèi)容只能是字符串)。所以,在Swift中有了可選類型的概念。(其實(shí)這一概念也是“借鑒”于其他編程語言,比如C#,只不過在C#中稱之為可空類型)。

大家在初看Optionals的感覺很陌生,在我第一眼看到它的時(shí)候,我就在想...這是什么鬼...但是仔細(xì)想想的話,可選值Optionals類型的引入,為我們也帶了便利.

可選值(optionals)無可爭議的是 swift 語言中最重要的特性之一,也是和其他語言,例如 Objective-C 的最大區(qū)別。通過強(qiáng)制處理那些有可能出現(xiàn) nil 的地方,我們就能寫出更有預(yù)測性的以及更健壯的代碼。

然而,有些時(shí)候可選值可能會(huì)致你于尷尬的境地,尤其是你作為開發(fā)者了解(甚至是有些猜測的成分在),有的特定變量始終是非空(non-nil)的,即使它是一個(gè)可選類型。例如,我們在一個(gè)視圖控制器中處理視圖的時(shí)候:

class TableViewController: UIViewController {
 var tableView: UITableView?
 override func viewDidLoad() {
  super.viewDidLoad()
  tableView = UITableView(frame: view.bounds)
  view.addSubview(tableView!)
 }
 func viewModelDidUpdate(_ viewModel: ViewModel) {
  tableView?.reloadData()
 }
}

這也是對于很多 Swift 程序員爭論比較激烈的地方,程度不亞于討論 tabs 和 spaces 的用法。有的人會(huì)說:

既然它是一個(gè)可選值,你就應(yīng)該時(shí)刻使用 if let 或者 guard let 的方式進(jìn)行解包。

然而另外一些人則采用完全相反,說:

既然你知道這個(gè)變量在使用的時(shí)候不會(huì)為 nil,使用 ! 強(qiáng)制解包多好。崩潰也要比讓你的程序處于一個(gè)未知狀態(tài)要好吧。

本質(zhì)上來講,我們這里討論的是要不要采用防御性編程(defensive programming)的問題。我們是試圖讓程序從一個(gè)未知狀態(tài)恢復(fù)還是簡單的放棄,然后讓它崩潰掉?

如果非得讓我對這個(gè)問題給出一個(gè)答案的話,我更傾向于后者。未知狀態(tài)真的很難追蹤 bug,會(huì)導(dǎo)致執(zhí)行很多不想執(zhí)行的邏輯,采用防御性編程就會(huì)使得你的代碼很難追蹤,出現(xiàn)問題很難追蹤。

但是,我不太喜歡給出一個(gè)二選一的答案。相反,我們可以尋找一些技術(shù)手法,用更精妙的方式的解決上面提到的問題。

它真的可選的嗎?

那些可選類型的,但是被代碼邏輯真實(shí)需要的變量和屬性,實(shí)際上是架構(gòu)瑕疵的一個(gè)體現(xiàn)。如果在某些地方確實(shí)需要它,但是它又不在,就會(huì)使得你的代碼邏輯處于未知狀態(tài),那么它就不應(yīng)該是可選類型的。

當(dāng)然,在某些特定場景下,可選值確實(shí)很難避免(尤其是和特定的系統(tǒng) API 交互的時(shí)候),那對于大部分這種情況,我們有一些技術(shù)來處理從而避免可選值。

lazy 要比非可選的可選值更好

某些屬性的值需要在其父類創(chuàng)建之后再生成(比如視圖控制器中的那些視圖,應(yīng)該在 loadView()或者 viewDidLoad()方法中被創(chuàng)建),對于這種屬性要避免其可選類型的方法就是使用 lazy 屬性。一個(gè)lazy屬性是可以是非可選類型的,同時(shí)也不在其父類的初始化方法里被需要,它會(huì)在其第一次被獲取的時(shí)候創(chuàng)建出來。

讓我們改一下上面的代碼,使用 lazy 來改造 tableView 屬性:

class TableViewController: UIViewController {
 lazy var tableView = UITableView()
 override func viewDidLoad() {
  super.viewDidLoad()
  tableView.frame = view.bounds
  view.addSubview(tableView)
 }
 func viewModelDidUpdate(_ viewModel: ViewModel) {
  tableView.reloadData()
 }
}

這樣,沒有可選值了,也不會(huì)有未知狀態(tài)咯🎉

適當(dāng)?shù)囊蕾嚬芾硪确强蛇x的可選值要好

可選值類型另外一種常用的場景就是用來打破循環(huán)依賴(circular dependencies)。有的時(shí)候,你就陷入 A 依賴 B,B 又依賴 A 的情況,如下:

class UserManager {
 private weak var commentManager: CommentManager?
 func userDidPostComment(_ comment: Comment) {
  user.totalNumberOfComments += 1
 }
 func logOutCurrentUser() {
  user.logOut()
  commentManager?.clearCache()
 }
}
class CommentManager {
 private weak var userManager: UserManager?
 func composer(_ composer: CommentComposer
     didPostComment comment: Comment) {
  userManager?.userDidPostComment(comment)
  handle(comment)
 }
 func clearCache() {
  cache.clear()
 }
}

從上面的代碼,我們可以看到,UserManager 和 CommentManager 之間有一個(gè)循環(huán)依賴的問題,它們二者都沒法假設(shè)自己擁有對方,但是它們都在各自的代碼邏輯里依賴彼此。這里就很容易產(chǎn)生 bug。

那要解決上面的問題,我們創(chuàng)建一個(gè) CommentComposer 來做一個(gè)協(xié)調(diào)者,負(fù)責(zé)通知UserManager 和 CommentManager二人一個(gè)評論產(chǎn)生了。

class CommentComposer {
 private let commentManager: CommentManager
 private let userManager: UserManager
 private lazy var textView = UITextView()
 init(commentManager: CommentManager,
   userManager: UserManager) {
  self.commentManager = commentManager
  self.userManager = userManager
 }
 func postComment() {
  let comment = Comment(text: textView.text)
  commentManager.handle(comment)
  userManager.userDidPostComment(comment)
 }
}

通過這種形式,UserManager 可以強(qiáng)持有 CommentManager 也不產(chǎn)生任何依賴循環(huán)。

class UserManager {
 private let commentManager: CommentManager
 init(commentManager: CommentManager) {
  self.commentManager = commentManager
 }
 func userDidPostComment(_ comment: Comment) {
  user.totalNumberOfComments += 1
 }
}

我們又一次的移除了所有的可選類型,代碼也更好預(yù)測了🎉。

優(yōu)雅的崩潰(Crashing gracefully)

通過上面幾個(gè)例子,我們通過對代碼做一些調(diào)整,移除了可選類型從而排除了不確定性。然而,有的時(shí)候,移除可選類型是不可能的。讓我們舉個(gè)例子,比如你在加載一個(gè)本地的包含針對你 App 的配置項(xiàng)的 JSON 文件,這個(gè)操作本身一定會(huì)存在失敗的情況,我們就需要添加錯(cuò)誤處理。

繼續(xù)上面這個(gè)場景,加載配置文件失敗的時(shí)候繼續(xù)執(zhí)行代碼就會(huì)使得你的 app 進(jìn)入一個(gè)未知狀態(tài),在這種情況下,最好的方式讓它崩潰。這樣,我們會(huì)得到一個(gè)崩潰日志,希望這個(gè)問題能夠在用戶感知之前早早的被我們的測試人員以及 QA 處理掉。

所以,我們?nèi)绾伪罎?。。。最簡單的方式就是添?! 操作符,針對這個(gè)可選值強(qiáng)制解包,就會(huì)在其是 nil 的時(shí)候發(fā)生崩潰:

let configuration = loadConfiguration()!

雖然這個(gè)方法比較簡單,但是它有個(gè)比較大的問題,就是一旦這段代碼崩潰,我們能得到的只有一個(gè)錯(cuò)誤信息:

fatal error: unexpectedly found nil while unwrapping an Optional value

這個(gè)錯(cuò)誤信息并不告訴我們?yōu)槭裁窗l(fā)生這個(gè)錯(cuò)誤,在哪里發(fā)生的,給不了我們什么線索來解決它。這個(gè)時(shí)候,我們可以使用 guard 關(guān)鍵字,結(jié)合 preconditionFailure() 函數(shù),在程序退出的時(shí)候給出定制消息。

guard let configuration = loadConfiguration() else {
 preconditionFailure("Configuration couldn't be loaded. " +
      "Verify that Config.JSON is valid.")
}

上面這段代碼發(fā)生崩潰的時(shí)候,我們就能獲得更多更有效的錯(cuò)誤信息:

fatal error: Configuration couldn't be loaded. Verify that Config.JSON is valid.: file /Users/John/AmazingApp/Sources/AppDelegate.swift, line 17

這樣,我們現(xiàn)在有了一個(gè)更清晰的解決問題的辦法,能夠準(zhǔn)確的知道這個(gè)問題在我們代碼里的哪個(gè)未知發(fā)生的。

引入 Require 庫

使用上面的 guard-let-preconditionFailure 的方案還是有一些冗長,確實(shí)讓我們呃代碼更難駕馭。我們也確實(shí)不希望在我們的代碼里占很多篇幅去些這種代碼,我們想更專注于我們的代碼邏輯上。

我的解決方案就是使用 Require。它只是簡單的在可選值添加簡單的 require() 方法,但能夠使得調(diào)用的地方更簡潔。用這種方法來處理上面加載 JSON 文件的代碼就可以這樣寫:

let configuration = loadConfiguration().require(hint: "Verify that Config.JSON is valid")

當(dāng)出現(xiàn)異常的時(shí)候,會(huì)給出下面的錯(cuò)誤信息:

fatal error: Required value was nil. Debugging hint: Verify that Config.JSON is valid: file /Users/John/AmazingApp/Sources/AppDelegate.swift, line 17

Require 的另一個(gè)優(yōu)勢就是它和調(diào)用 preconditionFailure() 方法一樣也會(huì)拋異常 NSException,就能使得那些異常上報(bào)工具能夠捕獲異常發(fā)生時(shí)候的元數(shù)據(jù)。

你如果想在自己代碼中使用的話,Require 現(xiàn)在在 Github 上開源了

總結(jié)

所以,總結(jié)來看,在 Swift 語言里處理那些非可選的可選值,我有幾點(diǎn)自己的貼心小提示給大家:

  • lazy 屬性要比非可選的可選值要更好
  • 適當(dāng)?shù)囊蕾嚬芾硪确强蛇x的可選值要好
  • 當(dāng)你使用非可選的可選值的時(shí)候,優(yōu)雅的崩潰

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Objective-C和Swift的轉(zhuǎn)換速查手冊(推薦)

    Objective-C和Swift的轉(zhuǎn)換速查手冊(推薦)

    這篇文章主要給大家介紹了關(guān)于Objective-C和Swift的轉(zhuǎn)換速查手冊的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),非常推薦給大家參考學(xué)習(xí)使用,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)不
    2018-06-06
  • Swift無限循環(huán)控件開發(fā)

    Swift無限循環(huán)控件開發(fā)

    這篇文章主要為大家詳細(xì)介紹了Swift無限循環(huán)控件開發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • 詳解Swift編程中的方法與屬性的概念

    詳解Swift編程中的方法與屬性的概念

    這篇文章主要介紹了Swift編程中的方法與屬性的概念,是Swift入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-11-11
  • 深入講解Swift的內(nèi)存管理

    深入講解Swift的內(nèi)存管理

    不管在什么語言里,內(nèi)存管理的內(nèi)容都很重要,Swift使用自動(dòng)引用計(jì)數(shù)來管理應(yīng)用程序的內(nèi)存使用。這表示內(nèi)存管理已經(jīng)是Swift的一部分,在大多數(shù)情況下,你并不需要考慮內(nèi)存的管理。本文主要介紹了Swift中內(nèi)存管理的相關(guān)資料,需要的朋友可以參考。
    2017-03-03
  • Swift教程之集合類型詳解

    Swift教程之集合類型詳解

    這篇文章主要介紹了Swift教程之集合類型詳解,Swift 提供兩種集合類型來存儲(chǔ)集合,數(shù)組和字典,本文詳細(xì)講解了數(shù)組的創(chuàng)建、讀取和修改數(shù)組、遍歷數(shù)組以及集合的操作等內(nèi)容,需要的朋友可以參考下
    2015-01-01
  • Swift學(xué)習(xí)筆記之邏輯分支與循環(huán)體

    Swift學(xué)習(xí)筆記之邏輯分支與循環(huán)體

    這篇文章主要給大家介紹了關(guān)于Swift學(xué)習(xí)筆記之邏輯分支與循環(huán)體的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-12-12
  • Swift在什么情況會(huì)發(fā)生內(nèi)存訪問沖突詳解

    Swift在什么情況會(huì)發(fā)生內(nèi)存訪問沖突詳解

    這篇文章主要給大家介紹了關(guān)于Swift在什么情況會(huì)發(fā)生內(nèi)存訪問沖突的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Swift中static和class關(guān)鍵字的深入講解

    Swift中static和class關(guān)鍵字的深入講解

    這篇文章主要給大家介紹了關(guān)于Swift中static和class關(guān)鍵字的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Swift HTTP加載請求Loading Requests教程

    Swift HTTP加載請求Loading Requests教程

    這篇文章主要為大家介紹了Swift HTTP加載請求Loading Requests教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Swift簡單快速的動(dòng)態(tài)更換app圖標(biāo)AppIcon方法示例

    Swift簡單快速的動(dòng)態(tài)更換app圖標(biāo)AppIcon方法示例

    這篇文章主要為大家介紹了Swift動(dòng)態(tài)更換app圖標(biāo)AppIcon的簡單快速方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06

最新評論