2014-12-23 34 views
31

可選項的全部原因是爲了防止命中分配給nil/null/none的變量導致運行時崩潰。因此,變量不能爲零;相反,它們可以封裝在一個可選類型中,將其表示爲Some或None,並解包以獲取Some或Nil的特定內容。在Swift中,如何避免可選項和零對象引用?

但是,如果你用!或隱式展開的選項將它們全部解開,那麼只需介紹由於運行時崩潰而導致代碼不完美的可能性。如果您使用if let來安全地解開它們,則可以避免崩潰,但是您仍然處於if let語句的範圍內,無法處理Some中的值,因此您仍然必須處理潛在的零案例。如果你使用?爲了調用方法暫時解包,它在完成時將被重新包裝,引入了多層分層可選包裝的混亂可能性。

所以:在我看來,除非必要(例如,在調用返回它們的框架方法時),否則要避免使用可選項。但是,如果我不使用可選項,那意味着我的對象引用必須是非零,並且我無法弄清楚如何處理由於某種原因或不應該存在或不存在的情況,一個分配給對象引用的值。

我的問題是:我如何避免需要零?它似乎需要一種不同的編程方法。 (或者我應該只是使用可選項,如果這就是我正在做的事情,它如何比簡單地將對象引用賦值爲空像其他語言一樣好?)

我知道這可能是一個主觀問題,但我應該問這個問題呢?我不是在試圖挑起或激起辯論,我真的想知道正確的方法是什麼,因爲我寫了更多的Swift代碼。

+6

曾聽說過面向鐵路的編程嗎?這裏有一篇文章解釋了f#中的概念http://fsharpforfunandprofit.com/posts/recipe-part2/ – mustafa

+0

偉大的問題,看看函數式編程和(可能)函數Swift – Nick

+6

如果您要投最後一票投票結束這個問題太主觀了,你能評論一下它有什麼特別的錯誤嗎?這個問題真的是一個我怎麼做,這個答案真的可以回答。如果你真的認爲它應該被關閉,我可以做些什麼來改進它? –

回答

83

你是對的,可選項可以是一種痛苦,這就是爲什麼你不應該過度使用它們。但是,它們不僅僅是你在使用框架時必須處理的事情。他們是一個難以置信的常見問題的解決方案:如何處理返回結果的調用,可能是好的,或者可能不會。

例如,取Array.first成員。這是一個方便的實用程序,它爲您提供數組的第一個元素。爲什麼當您撥打a[0]時能夠撥打a.first有用?因爲在運行時該陣列可能是空的,在這種情況下a[0]會爆炸。當然你可以預先檢查a.count - 但是再次

a。你可能會忘記,

and

b。這會導致相當難看的代碼。

Array.first通過返回可選項來處理此事。因此,在可以使用數組的第一個元素的值之前,您必須展開可選項。

現在,您的問題關於僅在if let塊內存在的解包值。設想一下並行代碼,檢查數組數量。它會是一樣的,對吧?

if a.count > 0 { 
    // use a[0] 
} 
// outside the block, no guarantee 
// a[0] is valid 

if let firstElement = a.first { 
    // use firstElement 
} 
// outside the block, you _can't_ 
// use firstElement 

當然,你可以做一些事情,比如在計數爲零的時候儘早返回函數。這有效,但有點容易出錯 - 如果你忘了這麼做,或者把它放在沒有發生運行的條件語句中?基本上,您可以使用array.first來做同樣的事情:在函數的早期檢查計數,然後再執行array.first!。但那!就像是給你的信號 - 要小心,你正在做一些危險的事情,如果你的代碼不完全正確,你會後悔的。

選項還有助於使選擇稍微漂亮。假設你想在數組爲空時默認值。取而代之的是:

array.count > 0 ? a[0] : somedefault 

,你可以這樣寫:

array.first ?? somedefault 

這是在幾個方面更好。它把重要的事情放在前面:你想要的價值是表達如何開始,然後是默認值。與三元表達式不同的是,它首先用檢查表達式觸發你,然後是你實際需要的值,最後是默認值。它也更加簡單 - 避免輸入錯誤更容易,並且不可能導致運行時爆發。

再舉一個例子:find函數。這將檢查值是否在集合中並返回其位置的索引。但是這個價值可能不會出現在收藏中。其他語言可能會通過返回結束索引(它不指向一個值,而是一個超過最後一個值)來處理這個問題。這就是所謂的「哨兵」值 - 看起來像一個常規結果的值,但實際上有着特殊的含義。像前面的例子一樣,在使用結果之前,你必須檢查結果是不是等於結束索引。但你必須知道這樣做。您必須查看find的文檔並確認它是如何工作的。

隨着find返回一個可選項,當你理解可選的習慣用法時,要意識到它所做的原因是因爲結果可能因爲顯而易見的原因而無效。關於上述安全性的所有相同的事情也適用 - 您不會意外忘記,並將結果用作索引,因爲您必須首先打開它。

也就是說,您可以過度使用可選項,因爲它們是必須檢查的負擔。這就是爲什麼數組下標不返回可選項 - 這會造成很多麻煩,不得不經常檢查和解包它們,尤其是當您知道您使用的索引是有效的事實時(例如,您處於一個用於循環數組的有效索引範圍的循環),人們會不斷地使用!,並因此混亂他們的代碼而沒有任何好處。但是,然後像第一個和最後一個這樣的幫助程序方法被添加,以涵蓋人們希望快速執行操作而不必首先檢查數組大小,但希望安全地執行操作的常見情況。

(SWIFT詞典,在另一方面,預計將通過無效標定期訪問,這就是爲什麼他們[key]方法並返回一個可選)

更妙的是,如果能夠完全避免故障的可能性。例如,當filter不匹配任何元素時,它不返回可選的nil。它返回一個空數組。 「很明顯,它會」,你可能會說。但是你會驚訝你經常看到有人像數組一樣提供返回值,而實際上他們只是應該返回一個空數組。所以你完全正確地說,除非有必要,否則你應該避免選擇性 - 這只是一個必要手段的問題。在上面的例子中,我會說他們是必要的,並且是更好的解決方案。

+11

這是一個非常深入的,深入的,針對性的答案,我感謝您花時間詳細說明實際使用情況,以確定何時或何時不使用可選項。謝謝! –

+4

奇妙的東西。你應該寫一個快速的博客或者其他東西;) – jrturton

+0

他已經寫了[一個](https://airspeedvelocity.net/)。 – Zaxter

13

或者我應該只使用自選,如果這是我在做什麼, 怎麼回事任何不是簡單地空任務更好反對 引用像其他語言有哪些?

如果你的計算可能需要返回一個「特殊」的值,那麼是的,在Swift中你應該使用可選項。它們比空的類型更好,因爲它們是明確的。很容易錯過一個指針可能是nil的情況,使用可選項更困難(但完全有可能)。

如果你用「如果讓」安全解開他們,避免碰撞,但你 被卡住的範圍內「如果讓」語句與 價值的工作,你仍然必須處理潛在的零案件。

這是一個功能。我的意思是,這是一個可選類型的全部要點:你必須處理這兩種情況(nil和非nil),你必須明確它。

查看Haskell's Maybe type and monad查看相似概念的示例。 Maybe類型與可選類型完全相同,Maybe monad使用這些可選值「操作」操作非常容易,而無需始終手動檢查空值。

+1

這有助於區分可選的顯式與非空引用的區別。謝謝。 –

+0

你可以評論句子「*如果你使用?暫時打開調用方法,它將在完成時被重新包裝,引入了多層分層可選包裝的混亂可能性。」*「?快捷選項不是一元的,而只是表現爲函數? – Bergi

+0

@Bergi,在Objective-C中,你可以發送消息給一個'nil'對象,並且沒有什麼不好的事情發生。 Swift中的可選鏈只是這個特性的一個通用版本。 AFAIK你不能將它用於比嵌套屬性訪問,鏈式方法調用等更復雜的任何事情。 (不知道這是否回答了關於單子和函數的問題,不幸的是我對函數式編程不太瞭解。) – zoul