Swift到底需不需要運行時了?
作者:Sergio De Simone
譯者:Rays
本文要點
運行時編程是 Objective-C 編程人員的重要工具,它是一些系統框架的基礎。
儘管運行時編程的確移除了不少的重複代碼,使開發人員可以編寫更為精簡的程序,但它是一把雙刃劍,可能會導致 bug 難以被發現。
Swift 早就提供了強大的工具去解決一些在 Objective-C 中要使用運行時編程的問題,這些工具體現為不同的方法,例如閉包、泛型和協議等。
Swift 核心團隊已著手為 Swift 添加強大的動態特性,該工作在 Swift 3 中就打下了基礎。
Swift 社區正在努力提供強大程序庫,方便 Swift 開發人員靜態地解決在 Objective-C 中被動態處理的問題。
以下為正文:
幾個月前,在 Objective-C/Swift 開發人員社區中開展了一場辯論。當時辯論的內容涉及 Swift 中動態特性的缺失,以及運行時編程特性在 Objective-C 中的重要地位,而這些到純 Swift 開發環境就缺失了。
Cocoa 基礎設計模式就是一個典型的例子,其中包括了 Responder Chain、NSUndoManager、KVC/KVO/Bindings 和 Core Data 等。為達到易於編程和高度解耦,這些設計模式深度借鑒了 Objective-C 的動態特性。
辯論不僅局限於 Swift 是否是一種動態語言,其中關注的問題還涉及:對於運行時編程,看上去 Swift 並未提供任何類似的特性;以及看上去 Apple 從未討論過 Swift 中需要或值得具備哪些特性,以使用 Objective-C 的動態性可以在 Swift 中同樣可行。
為獲取更多的深層信息,InfoQ 訪談了 Chris Eidhof 和 Drew Crawford。Chris Eidhof 是《Functional Swift and Advanced Swift》一書的作者。Drew Crawford 是 Deckset 和 Scenery 的創立者,也是一名 Swift 開發人員,是首個靜態鏈接 Swift/Linux 程序的開發者。
InfoQ:你們能介紹一下動態特性在 Objective-C 和 Cocoa 編程中的重要性嗎?
Chris Eidhof:動態編程是一個被過度使用的詞,即便僅限於 Objective-C 和 Cocoa 環境。對於部分開發人員,它意味著程序在運行中顯示出了動態的行為。而對於另一部分開發人員,它可以指代對 KVO/KVC 的使用,或是延遲綁定(Late binding)、子類化(Subclassing)、Swizzling、運行時轉化(Runtime casting)、動態類型(Dynamic typing)等。基於不同的交談對象,動態編程還可能是上述技術的一個擴展子集。
在 Cocoa 中論及「動態編程」時,通常是指代運行時編程。在 Objective-C 中,使用 Runtime 已成為大多數框架的重要基礎。使用運行時編程我們可以編寫更精簡的程序,移除大量的重複代碼。雖然代碼及樣板文本的精簡是件好事,但是這也具有相應的風險,即在依賴於語言的特性時很難在編譯期靜態地發現缺陷。缺陷只有在運行時才會被發現。
不同於 Cocoa,UIKit 早已拋棄了運行時編程。綁定不再存在,並且與 Cocoa 相比,KVO/KVC 也不再重要了。Swift 在這個方向走得更遠一些。
Drew Crawford:動態特性在運行時改變程序的行為,例如鴨子類型(Duck-typing,轉化相似命名的方法所實現的無關類型)、在運行時創建新類、Swizzling 方法(在運行時改變函數)、選擇器(在運行時確定所調用的函數)等。
Cocoa 是圍繞著諸如此類的特性而設計的,其基本原則之一是假定存在著這些特性。Cocoa 的「目標 - 行為」模式的關鍵是選擇器,而字元串是 NSCoding 的關鍵所在。過去的數年中,這些動態模式已深深地融入到了 Cocoa 框架中的數百個細微之處。
InfoQ:Swift 在哪些方面存在短板?哪些問題易於用 Objective-C 解決而難以用 Swift 實現?
Chris Eidhof: Swift 中的「反射」特性仍然十分不成熟,部分運行時特性依然完全不可用。例如,在 Objective-C 中可以用 KVO 發現對象屬性,而在 Swift 中,KVO 只能與 Objective-C 對象一起使用,不能與結構一起使用。
Swift 中不少事情難以用面向運行時的方法實現。在捨棄這些運行時特性時,最難做到的是學會如何做另類思考。其實 Swift 可用更少的代碼解決幾乎所有的問題,我們需要的是另闢蹊徑。不使用運行時編程,我們還可以使用閉包、泛型、協議等特性,用更短的代碼、更安全的方式解決同樣的問題。
Drew Crawford: XCTest 就是一個的典型例子。它是 Xcode 的測試框架,通過運行時搜索所有以「test」開始的函數,找到你所編寫的新測試用例。如果沒有該特性,為讓 XCTest 知道如何調用你的測試,必須要手工維護所有測試用例的列表。現實中列表很容易發生遺漏,這樣遺漏的測試就得不到運行,導致你認為已測試的代碼事實上並未得到測試。
Swizzling 是應對第三方 SDK 出現 bug 的常用方法(可能會一些人指出這種用法是不對的)。當閉源 SDK 發生問題時,項目往往會深陷其中。通過利用運行時特性,你可以嵌入自己的代碼去繞過問題。該做法的問題在於,在 SDK 更新後,程序也必須做相應的修改,因此這種應對方法是一把雙刃劍。
InfoQ:你們是否設想過一種更為動態的 Swift?是否能在語言或架構中添加一定程度上的動態?Swift 是否應該做到完全動態?
Chris Eidhof:動態分發和消息傳遞是早就可用的特性,它們藉助了類或協議的使用。或許更多地支持對運行時編程會是十分有用的,但可能要在安全和性能上付出代價。就我個人而言,自 Swift 公開發布以來我就在全職地編寫 Swift 代碼,但我並不懷念運行時編程特性。
有別於依賴運行時編程,我採用了其它所有允許的動態行為特性,包括閉包、高階函數、協議、泛型等。使用這些特性讓我具備了動態的行為能力,並且還能利用用編譯器做靜態檢查。在我們所著的《Advanced Swift》一書中展示了所有這些在實踐中用到的技術。
我在近期的一個博客帖子中介紹了我們如何替換運行時編程(正是它使得 Objective-C 動態)為函數(正是它使得 Swift 動態)。我通過僅僅使用函數就重新實現了 NSSortDescriptor API:
http://chris.eidhof.nl/post/sort-descriptors-in-swift/
Drew Crawford: Swift 並不是不能用運行時編程。iOS/macOS 環境自帶完全可用的 Objective-C 運行時,你可將 Swift 代碼成功地橋接到運行時,在 Swift 中做 dispatch、選擇器和 Swizzling。
而最大的問題在於如何在其它的平台上實現同樣的事情,例如 Linux 等這些不具有 Objective-C 傳統的平台。我們打算讓越來越多的程序可在各大平台間移植,但讓程序員只寫可移植的代碼是很困難的。這個問題在很大程度上並未得到解決。
但是我認為必須率先考慮 Swift 為什麼誕生。Swift 並非是具有現代語法的 Objective-C。如果是這樣的話,那麼 Swift 項目就非常容易實現(在 Objective-C 的發展歷史上已經做過多次這樣的更新)。如果你想的是讓 Swift 更像 Objective-C,那麼只能說你並不需要一種新的語言、新的運行時、新的編譯器、新的基金會。
相反,Swift 代表著這樣一個結論,即 Objective-C 所走的路並非是我們想要的。我們想要的語言並非來自於接下來十年中每一屆 WWDC 大會中將會給出的增量改進,唯一的實現方法是去重新審視那些十分基礎的想法,去大量地重寫代碼,去改變我們曾經對 OC 語言的痴狂。
我的意思並非是要將動態特性作為眾矢之的,但我的確認為應拋開「如果 Objective-C 做了那麼 Swift 也應做到」的這種理念。喜歡 Objective-C 的開發人員就繼續使用它吧!Swift 需要規劃自身的發展藍圖。它需要了解歷史,也需要從中學習,這意味著以 Swift 要以自己的方式做事。
InfoQ:實現更具動態特性的 Swift 的挑戰有哪些?在不降低 Swift 安全性的條件下,如何解決這些挑戰?
Chris Eidhof:我並不認為會喪失過多的特性,但我還是要指出兩點。第一,為反射添加更多的支持將會是十分有幫助的,但這可能會成為雙刃劍,它也易於導致編寫不正確的代碼。
第二,也是更為重要的一點,我切實地期待著協議中的條件符合特性 (conditional conformance),它將使開發人員更具有表達力。例如,數組一般不遵從 Equatable,原因在於這要求數組中的所有元素都滿足 Equatable。以前該條件在語言中是無法表述的,但是現在 Swift 團隊正優先考慮添加條件符合。
條件符合還將允許開發人員做數據類型泛型編程(Datatype-generic Programming),這是一種已經被 Haskell 等語言使用的方法。數據類型泛型編程是一種類型安全的編程方法,執行開發人員數據結構上的操作(類似於運行時編程)。
Drew Crawford:性能和安全性是困擾 Objective-C 的兩個主要問題。很多人並不清楚,這兩個問題是由 Objective-C 的動態直接導致的。
在 Objective-C 這樣高度動態的語言中,編程人員的權力大到異乎尋常。他可以打開核心系統庫並置入自己的代碼。可以 Hook 到方法調用中的每一個組成環節,創建具有無窮個方法的對象,或是在對象的生命周期中插入和移除方法。這樣的權利真是匪夷所思呀。
但是能力越大,責任也就越大。作為編程人員你所具有的權力越大,留給編譯器的權力就相應地減少了。這時就像是給 Objective-C 編譯器戴上眼罩,將你的代碼「看成」類似於在數組上的一個基本 for 循環。但是我們如何才能知道你並未將 NSArray 替換為具有無窮個方法的另一些對象?我們怎麼能知道數組並非程序生成的並具有不限量元素?這些問題聽上去十分瘋狂,但是在 Objective-C 中是「合法上路」的,無法避免會有編程人員這麼做。除非編譯器屈從於你所編寫的代碼,否則它幾乎無法在你的程序中成功編譯一行代碼,它必須按你代碼的編寫方式工作。而你編寫的代碼不可能總是很快的或是非常安全的。
Swift 收回了對編程人員具有無限權力的許可。在此交易中,我們得到了摘掉遮眼罩的編譯器。它可以更深入地審視你的代碼,通常會具有整個程序的鳥瞰圖。這使它成功地實現了驚人的優化,並且可對遠距離組件間交互等過程中的細微缺陷進行報警。一旦編譯器能做的不再僅限於語法和括弧風格之類的事情,就更能吸引人們去使用 Swift 了。正是這樣的決定給了 Swift 現在的個性。
其中所存在的難題是,我們如何在支持 Objective-C 開發人員所需特性的同時,維持 Swift 開發人員所期望的速度和安全性。事實上這個目標不可能達成,但是如果群策群力,要接近它還是可以的。
InfoQ:為使 Swift 更適合於解決上述的關注問題,你們是否了解 Swift 開發中已經做了或是正在做哪些工作?
Chris Eidhof:是的,幾乎所有討論都是發生於郵件列表中的,Github 的 Swift-evolution 代碼庫也是一個非常好的資源。如果覺得其中的內容過於龐雜,可以去關注一個稱為「Swift Weekly Brief」的每周簡報。
Drew Crawford: 舉一個實例,就是在 Swift 3 中所引入的「_typeByName」。它對應於 Objective-C 中的「NSClassFromString」解決方案,允許動態構建類。
不過,API 前面的下劃線會讓你意識到,這還只是正在進行中的語言設計,並沒有完全考慮清楚,將來可能發生改變。
InfoQ:Swift 的目標在於具備處理廣泛問題的能力,包括伺服器端開發和系統編程。從整體上看,解決這些問題對於 Swift 的重要性如何?你們是否認為 Swift 開發團隊在聽取意見?
Chris Eidhof: Swift 團隊確實正在很好地聽取意見。該團隊活躍於所有的 Swift 郵件列表中,並深入參與了社區。Swift 是一種雄心勃勃的語言,我認為 Swift 團隊的確發現了雄心和實用主義間的最有效平衡點。但是,聽取意見並不意味著要把每個可能的特性添加到 Swift 中。我印象十分深刻的是,Swift 團隊經常是後退一步,盡量去理解問題真正所在,而非只是實現「增加一個特性」。
Drew Crawford:除了動態問題,Swift 現在還面對著很多挑戰,例如核心函數的語法和命名還非常不穩定、不具備 ABI 兼容性、泛型沒有完成等。這些問題十分嚴重,就像一棟建築正在燃燒。在我看來,事實上核心團隊十分關注這些問題。
在得到實現動態特性的建議後,團隊從來沒有說過要「不做」。團隊的答覆是:動態問題是一個重要的問題,但是在被其它的問題所干擾的情況下,該問題遠非在當下所能得到解決的。我與 Objective-C 開發人員感同身受,他們苦惱於沒有更好的動態解決方案,必須又得年復一年地忍受該問題。但是我認為如果連續每年都有超過大半的標準庫被重命名,他們會更為苦惱。
應注意到,Apple 所維護 Objective-C 代碼多於世界上任何其它人。Core Data 自身所暗藏的解決動態問題的高招可能要多於其它所有的生態系統的總和。因此看上去 Apple 對動態問題一無所知是一種誤導,Apple 比其它任何人都更好地了解該問題,包括動態的優點和代價。
但是我認為我們也應該去做些事情。Swift Way就是首要以靜態方式去考慮解決那些在 Objective-C 中被動態解決的問題,並在其它的方法不成功時引入一些動態特性。如果某些程序更適合用 Objective-C 表示,那麼我們就需要與這些程序和平共處,因為我們中的很多人也十分清楚,同樣會有不少程序更適合於用 Swift 開發。正是這些差異賦予了一種語言其品質和個性,我們需要允許各種語言去做最好的自己,找到適合語言自身的發展道路。
Swift 的發展道路依然是一個十分開放的問題。我很有信心 Swift 將會包括比任何現有語言還要多的動態特性。如果把 Swift 比作一本書,本書的故事依然在發展,鑒於社區正在完成第三章,我認為現在對這本書如何結尾做出定論還為時尚早。
結論
在本文中,我們探討了一些關於 Swift 缺失動態特性的問題,這些問題是 Objective-C 編程人員所關注的。我們訪談的兩位開發人員深入地參與了推進 Swift 語言及其軟體庫生態系統的過程,這將有助於我們理解問題的發生場景,以及最終 Swift 將如何解決這些問題。
關於被訪者
Drew Crawford是一位軟體開發人員、作家和顧問。他編寫了首個靜態鏈接的 Swift/Linux 程序。除了編寫 Swift 程序,他還在 Austin 運作了一家精品開發公司,為不同規模的企業編寫 iOS 應用和伺服器軟體,並授權定製的 Swift 技術。他所著的《Why Mobile Web Apps are Slow》一書被廣為閱讀,撰寫的文章也已被翻譯為多種語言,並被指定為全球多所大學的移動開發教學中的指定讀物。
Chris Eidhof是在柏林的一位 Swift 開發人員。他創立了 objc.io 和一系列的會議,並是圖書《Functional Swift》和《Advanced Swift》的作者,同時還是 Deckset 和 Scenery 的創立者。在豐富的業餘時間中,他喜歡跑步。
活動推薦


※音視頻社交中回聲消除技術是如何實現的
※Android代碼重構實戰
TAG:移動開發前線 |
※iPhone 到底需不需要貼膜和帶套?
※Thrasher的人需不需要會滑板
※DHA到底需不需要補?重要嗎?
※老款iPhone需不需要升級iOS 12?看看實測速度對比結果!
※不去角質用lamer也不管用!5點自測你需不需要去角質!
※用wifi時需不需要關閉移動網路?此辦法大多數人都不知道
※iOS 12.1.4發布了,你的蘋果手機到底需不需要更新?
※Condi事件引發熱議:電競需不需要法律約束?
※5G來了,我們的手機到底需不需要更換?中國移動給你答案
※IT男到底需不需要護膚?
※頭痛到底需不需要做CT檢查?
※運動到底需不需要出汗?
※健身時,到底需不需要腰帶?
※即將來臨的5G時代,手機和SIM卡需不需要更換,已有準確回答
※LOL:曾讓Faker替補的世界冠軍竟淪為混子,LPL到底需不需要韓援?
※你到底需不需要去角質?
※人到底需不需要養生,你說呢
※寶寶到底需不需要補DHA?看完你就知道了!
※你的智齒到底需不需要拔除?
※防晒霜到底需不需要卸?