Swift 為什麼沒有異常處理?

個人感覺異常處理非常重要(難道不重要?)


「異常處理」和「使用 try/catch 來處理異常」是馬與白馬的關係。Swift 有異常處理,或者說使用 Swift 當然可以處理異常,只不過不是用 try/catch 這樣的機制或語法。C 沒有 try/catch,Perl 沒有,Lua 沒有,Go 也沒有,但是這些語言都有別的機制處理異常,大家照樣用得好好的。

即便是提供了 try/catch 機制的語言,對待它的態度也不大一樣。Java / C# 裡面 try/catch block 屬於要節制使用的工具,用 try/catch 來決定 control flow 更是欺師滅祖的罪名;可在 Python 裡面 try/except 常年兼職做 if-else,Pythonista 們眉頭都不皺一下——這跟語言的設計理念和拋異常的性能有關。但 try/catch 機制的主要問題可能是容易被濫用,初學者把有問題的代碼塊包起來 try 一腳再 catch 住萬惡的異常基類,哇噻程序頓時不崩潰了腰不疼了腿不酸了魯棒了 bug free 了。可即便意識到這樣做是鴕鳥式的 anti-pattern,又會發現 try/catch 屬於「很容易上手但很難成為高手」的特性,仍舊會在「根本不確定能踹到什麼,所以也總要推敲到底能抓到什麼」的階段徘徊。許多高手因此相當排斥 try/catch [1][2][3]。

以蘋果開發的角度來說,異常處理可以細分成對於「錯誤(error)」和「異常(exception)」兩類情況的處理,前者是可以預見到發生可能性的、可以恢復的非致命錯誤,比如找不到指定位置的文件或者網路忽然斷開之類,在 Objective-C 中由 NSError 代表,Java 中的對應物是 checked exception,基類是 lang.Exception;後者則是無法預見通常也不可恢復的致命錯誤,比如磁碟壞了、內存沒了之類,在 Objective-C 中由 NSException 代表,Java 中則是 RuntimeException 這樣的所謂 unchecked exception[4]。Objective-C 中對於前者的處理是判斷可能產生錯誤的方法的返回值,並傳入一個 NSError ** 類型的指針指針去獲取生成的 NSError 對象來看看錯誤究竟是什麼,對於後者才使用 @try/@catch;而 Java / C# 中則是兩者均使用 try/catch 處理。

但問題是 Objective-C 雖然有 @try/@catch,蘋果的範例代碼中卻很少使用到它(至少是在 iOS 部分),蘋果對 try/catch 的態度也是限制用在「programming or unexpected runtime errors」[5],像在 Java 里那樣用 try/catch 在 Obj-C 中完全行不通。而且 Obj-C 可能出現的異常也不是全都能由 NSException 代表,有些異常必須在 C 級別 trap。總之,try/catch 不是蘋果鼓勵的開發範式,Cocoa 程序員也極少用它,甚至你會覺得蘋果開發者們對 exception 的態度就是「反正也不知道該怎麼處理索性就讓程序 crash 好了」(我覺得這個態度挺好的,fail fast 是件好事[6]),所以專為 Cocoa 開發而推出的 Swift 沒有 try/catch 也並不是很意外的事情。當然,我也不敢斬釘截鐵地說未來版本里 try/catch 肯定不會出現(二〇一五年六月八日更新:Swift 2 已經有了 do{} catch{} ),因為 Swift 畢竟和 Obj-C 共享運行時,如果 Swift 有朝一日也會要拿來寫一些不得不處理底層拋上來的 NSException 的東西,那就必須能提供某種錯誤恢復機制,而不是簡單地 crash。

(Swift 處理 NSError 的範式與 Objective-C 基本一致,而且有 optional 的助益,變得更簡潔了。)

又,開發者論壇上關於這個話題已經有很長的討論: https://devforums.apple.com/thread/227375 (需登錄)

[1] Cleaner, more elegant, and wrong 以及 Cleaner, more elegant, and harder to recognize
[2] http://www.joelonsoftware.com/items/2003/10/13.html
[3] https://groups.google.com/forum/#!topic/nodejs/1ESsssIxrUU
[4] Java 中 RuntimeException 繼承自 Exception,Obj-C 中 NSError 和 NSException 各自繼承自 NSObject。另外 Java 還有一個 lang.Error,並不繼承自 Exception,也可以視為一種 unchecked exception。
[5] Exception Programming Topics: Introduction to Exception Programming Topics for Cocoa
[6] http://martinfowler.com/ieeeSoftware/failFast.pdf


剛寫了一篇關於Swift 2.0 異常處理的文章,希望有幫助


謝邀

對於unchecked exception來說,基本上每行調用其他函數的語句都存在拋出異常的可能性。這對於分析代碼的健壯性是個巨大的思維負擔(你必須要考慮每一行出現異常怎麼辦),基本上沒幾個人能做到。對於存在複雜對象圖的程序,幾乎做不到在任何時刻都能完美維護核心對象圖的完整性(而一旦完整性被破壞,會導致任意後果的惡性bug)

如果取消異常,就像C一樣,雖然處理「錯誤情況」的代碼會冗長,但是至少分析起來容易(代碼永遠都是老老實實線性往下走,不會突然跳出)。大部分C程序比C++程序可靠的原因也在於此


swift在2.0版本添加了錯誤的處理。具體處理方式如下:

func loadData() throws { }

func test() {
?do {
?try loadData()
} catch {
?print(error)
}
}


咳咳, APP 不需要異常處理。 Swift 本身的 optional binding 保證了長串調用的可用性。

if a.prop1?.prop2?.method1()?.method2()? {
這裡做正常處理
} else {
告訴用戶(「網路錯誤")
}


無疑是在增加程序員的工作量,技術的退步,異常不僅僅是補獲,而是一種思想,調用者使用一個函數時,函數發現參數錯了,你應該拋出異常還是返回默認值,還是任由程序崩潰?我選擇拋出人性化的異常,避免後期更多的運行時錯誤!各行其事,出錯了我告訴你,你不用在意黑匣子是怎麼工作的,總之黑匣子總會給你反饋必要的信息,相反,任由程序奔潰或返回默認值,這就是要程序員處處考慮並且熟悉黑匣子的工作原理,這還談什麼技術積累和發展!!!!絕對反駁


只想指出,在Java裡面try catch不是不被鼓勵使用的.恰好相反,框架級別的代碼都有完整的exception框架設計.
Objective-C裡面NSError是最常使用的,的確極少使用try catch.


swift引入了rethrow來處理閉包拋出的異常,很簡潔。

當我們使用Swift異常處理時,應該不認為它是拋出異常,而是返回不同的類型:正確的和錯誤的,這樣才更合適一些


OC異常處理就很噁心了,能catch住的異常有限,還影響性能,項目中很少用
Swift和OC共用一個Runtime,即便加了異常處理,也是和OC同樣的方式,有和沒有沒啥區別
況且Swift相比OC編碼要安全不少


&>&>總之,try/catch 不是蘋果鼓勵的開發範式,Cocoa 程序員也極少用它,
&>&>甚至你會覺得蘋果開發者們對 exception 的態度就是「反正也不知道該怎麼處理索性就讓程序 crash 好了」

這是什麼態度,直接讓程序crash?
do {} catch{} 居然捕獲不到unchecked exception.
從java過來的,表示swift的設計很弱


推薦閱讀:

TAG:編程語言 | iOS 8 | Swift 語言 |