Swift和Objective-C的运行时编程
本文要點
\\- 運行時編程是Objective-C編程人員的重要工具,它提供了一些系統框架的基礎。\\t
- 盡管運行時編程的確移除了不少的樣板文本(Boilerplate),使開發人員可以編寫更為精簡的程序,但它是一把雙刃劍,可能會導致軟件缺陷難以被發現。\\t
- Swift早就提供了強大的工具去解決一些在Objective-C中要使用運行時編程的問題,這些工具體現為不同的方法,例如閉包、泛型和協議等。\\t
- Swift核心團隊早已著手為Swift添加強大的動態特性,該工作在Swift 3中就打下了基礎。\\t
- Swift社區正在努力提供強大程序庫,方便Swift開發人員靜態地解決在Objective-C中被動態處理的問題。\
幾個月前,在Objective-C/Swift開發人員社區中開展了一場辯論。當時辯論的內容涉及Swift中動態特性的缺失,以及純Swift開發環境所難以復制的運行時編程特性在Objective-C中的重要地位。
\\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中,使用運行時已成為大多數框架的重要基礎。使用運行時編程我們可以編寫更精簡的程序,移除大量的樣板文本。雖然代碼及樣板文本的精簡是件好事,但是這也具有相應的風險,即在依賴于語言的特性時很難靜態地發現缺陷。缺陷只有在運行時才會被發現。
\\不同于Cocoa,UIKit早已拋棄了運行時編程。綁定不再存在,并且與Cocoa相比,KVO/KVC也不再重要了。Swift在這個方向走得更遠一些。
\\Drew Crawford:動態特性在運行時改變程序的行為,例如鴨子類型(Duck-typing,轉化相似命名的方法所實現的無關類型)、在運行時創建新類、Swizzling方法(在運行時改變函數)、選擇器(在運行時確定所調用的函數)等。
\\Cocoa是圍繞著諸如此類的特性而設計的,其基本原則之一是假定存在著這些特性。Cocoa的“目標-行為”模式的關鍵是選擇器,而字符串是NSCoding的關鍵所在。過去的數年中,這些動態模式已深深地融入到了Cocoa結構中的數百個細微之處。
\\\InfoQ:Swift在哪些方面存在短板?哪些問題易于用Objective-C解決而難以用Swift實現?
\\\Chris Eidhof: Swift中的“反射”特性(Reflection)仍然十分不成熟,部分運行時特性依然完全不可用。例如,在Objective-C中可以用“鍵值觀察者”(Key-Value Observing ,KVO)發現對象屬性。在Swift中,KVO只能與Objective-C對象一起使用,而不能與結構一起使用。
\\Swift中不少事情難以用面向運行時的方法實現。在舍棄這些運行時特性時,最難做到的是學會如何做另類思考。其實Swift可用更少的代碼解決幾乎所有的問題,我們需要的是另辟蹊徑。不使用運行時編程,我們還可以使用閉包、泛型、協議等特性,用更短的代碼、更安全的方式解決同樣的問題。例如,在線視頻教學Swift Talk的“Networking”一節展示了如何安全地使用泛型為Web服務提供一個十分動態的接口。
\\Drew Crawford: XCTest就是一個的典型例子。它是Xcode的測試框架,通過運行時搜索所有以“test”開始的函數,發現你所編寫的新測試。如果沒有該特性,為讓XCTest知道如何調用你的測試,必須要手工維護所有測試的列表?,F實中列表中的測試很容易被遺漏,這樣遺漏的測試就得不到運行,導致你認為已測試的代碼事實上并未得到測試。
\\Swizzling是應對軟件提供商缺陷的常用方法(可能會一些人指出這種用法是不對的)。當閉源軟件庫存在問題時,項目往往會深陷其中。通過在運行時圍繞問題構建定制的環境,你可以嵌入自己的代碼去應對這樣的問題。該做法的問題在于,在軟件提供商做更新后,程序很容易被破壞,因此這種應對方法是一把雙刃劍。
\\\InfoQ:你們是否設想過一種更為動態的Swift?是否能在語言或架構中添加一定程度上的動態?Swift是否應該做到完全動態?
\\\Chris Eidhof:動態分發和消息傳遞是早就可用的特性,它們借助了類或協議的使用。或許更多地支持對運行時編程會是十分有用的,但可能要在安全和性能上付出代價。就我個人而言,自Swift公開發布以來我就在全職地編寫Swift代碼,我至始至終地運用著運行時編程特性。
\\有別于依賴運行時編程,我采用了其它所有允許的動態行為特性,包括閉包、高階函數、協議、泛型等。使用這些特性讓我具備了動態的行為,但依然用編譯器做靜態檢查。在我們所著的《Advanced Swift》一書中展示了所有這些在實踐中用到的技術。
\\我在近期的一個博客帖子中介紹了我們如何替換運行時編程(正是它使得Objective-C動態)為函數(正是它使得Swift動態)。我通過僅僅使用函數就重新實現了NSSortDescriptor API。
\\Drew Crawford: Swfit已具有了不少的動態特性。iOS/macOS環境自帶完全可用的Objective-C運行時,你可將Swift代碼成功地關聯到運行時,在Swift中做動態發布、選擇器和Swizzling。
\\而最大的問題在于如何在其它的平臺上實現同樣的事情,例如Linux等這些不具有Objective-C傳統的平臺。我們打算讓越來越多的程序可在各大平臺間移植,而開發人員在寫新代碼時堅持這些可移植的技術是些很有壓力的事。這個問題在很大程度上并未得到解決。
\\但是我認為必須率先考慮Swift存在的合理性。Swift并非是具有現代語法的Objective-C。如果是這樣的話,那么Swift項目豈非輕易就可實現(該語言的發展歷史中,在很多點上的確是這么做的)。如果你正致力于此,那么只能說你并不需要一種新的語言、新的運行時、新的編譯器、新的基金會。
\\相反,Swift做出了這樣的一個結論,即Objective-C所走的路并非我們想要的。我們想要的語言并非來自于接下來十年中每一屆WWDC大會中將會給出的增量改進,唯一的實現方法是去重新審視那些十分基礎的想法,去大量地重寫代碼,去殺掉我們曾心愛的代碼。
\\我的意思并非是要將動態特性作為眾矢之的,但我的確認為應拋開“如果Objective-C做了什么那么Swift也應做到”的這種理念。喜歡Objective-C的開發人員就繼續使用它吧!Swift需要規劃自身的發展藍圖。它需要了解歷史,也需要從中學習,這意味著以Swift要以自己的方式做事。
\\\InfoQ:實現更具動態特性的Swift的挑戰有哪些?在不降低Swift安全性的條件下,如何解決這些挑戰?
\\\Chris Eidhof:我并不認為會喪失過多的特性,但我還是要指出兩點。第一,為反射添加更多的支持將會是十分有幫助的,但這可能會成為雙刃劍,它也易于導致編寫不正確的代碼。
\\第二,也是更為重要的一點,我切實地期待著協議中的條件符合特性,它將使開發人員更具有表達力。例如,數組一般不遵從Equatable,原因在于這要求數組中的所有元素都滿足Equatable。以前該條件在語言中是無法表述的,但是現在Swift團隊正優先考慮添加條件符合。
\\條件符合還將允許開發人員做數據類型泛型編程(Datatype-generic Programming),這是一種已經被Haskell等語言使用的方法。數據類型泛型編程是一種類型安全的編程方法,執行開發人員數據結構上的操作(類似于運行時編程)。
\\Drew Crawford:性能和安全性是困擾Objective-C的兩個主要問題。很多人并不清楚,這兩個問題是由Objective-C的動態直接導致的。
\\在Objective-C這樣高度動態的語言中,編程人員的權力大到異乎尋常。他可以打開核心系統庫并置入自己的代碼。可以鉤到方法調用中的每一個組成環節,創建具有無窮個方法的對象,或是創建在對象生命周期中反復閃現的方法。這樣的權利真是匪夷所思呀。
\\但是能力越大,責任也就越大。作為編程人員你所具有的權力越大,留給編譯器的權力就相應地減少了。這時就像是給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缺失動態特性的問題,這些問題是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的創立者。在豐富的業余時間中,他喜歡跑步。
\\\\查看英文原文:Swift and Objective-C Runtime Programming
\\感謝冬雨對本文的審校。
\\給InfoQ中文站投稿或者參與內容翻譯工作,請郵件至editors@cn.infoq.com。也歡迎大家通過新浪微博(@InfoQ,@丁曉昀),微信(微信號:InfoQChina)關注我們。
總結
以上是生活随笔為你收集整理的Swift和Objective-C的运行时编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 查询时注意 查询字段传值参数类型,尽量和
- 下一篇: 模式 - 收藏集 - 掘金