使用 Objective-C 一年後我對它的看法

oschina發表於2013-12-24

  我在一年前因需要將RedPhone專案從Android移植到iOS而首次接觸Objective-C。大約一個月前我負責的部分(後端:聲音,網路,加密部分)已經完成。我們正等著外部的安全審查,同時在內部繼續完成UI工作的過程中並未發現任何後端的bug(言外之意開發質量高)。在RedPhone最終釋出後,我對於這次工作中哪些做錯了,基於android和ios的程式碼都有哪些不同等等做了討論。今天想探討一下去年過程中關於Objective-C的一些體驗。

  一年前我從未接觸過Objective-C.(一行程式碼也沒看過)我是名C#開發。現在我認為自己是名中級Objective-C開發:

  雖然還有很多知識上的漏洞但我在現在使用這門語言已算是得心應手。比如,大家都知道不能把nil賦給陣列和其他的集合型別,但就在上週我還不知道可以直接插入NSNull來實現。當然了,我只是假設大家只是不會把nils賦給某個集合。(意味著大部分人也不知道這個workaround)

  當我討論語言時,我聽起來會傾向於比較挑剔,所以請記住這篇文章不是指控Objective-C無法有效的處理問題。這篇文章是關於我使用Objective-C的經驗,它和其他語言不同的地方,以及我遇到的問題。這是我的觀點,因為程式設計領域充滿了關於哪種語言更好的完全對立的觀點(e.g. 動態 vs 靜態型別程式語言),你可以相信,這僅僅是以一對多。一個相關的引用:

僅有兩種語言:一種是大家抱怨的,另一種是沒人使用的。 —— Bjarne Stroustrup

  概述

  Object-C是C語言的超集,所以很自然的讓我注意的第一件事是它哪裡和C不同。基本上你在以Obj-C風格寫程式碼時唯一做的類似C的事情(C-ish stuff)是使用標頭檔案和使用前進行型別宣告。

  奇怪的是,Objective-C使我想起的更多的不是C而是Visual Basic 6。至少,是在我每日使用所遇到的問題上。Obj-C和VB6兩者都 喜歡讓程式保持 執行下去(即使出錯了,譯者注)。這是不是個奇怪的現象?程式可能會錯過打破當前狀態的問題。另外,兩種語言使用引用計數來清理記憶體,這會隨著你的深入造成更多記憶體洩露。永遠警惕引入迴圈引用。最後,兩者沒有泛型型別。把這些話都直直的裝入你的腦子裡,否則你接下來的日子會很難過。

  還有另一件Obj-C與VB的包袱,例如,在Objective-C中許多物件有 “core foundation”、 “new style” “NeXTSTEP”的形式,而自動引用計數技術早在兩年前就已滲透到整個社群了,當然了,還有C的子集。所以,大多數情況下我們可以忽略這些歷史包袱,但它們的出現,我並不是暗示說Obj-C與VB是相同的程式語言,它們有很大不同,首先,語法就有很大的不同。

  語法

  Objective-c的語法很特別。傳遞一個訊息(如呼叫個方法),你需要用方括號把接收訊息的物件以及訊息括起來。訊息由引數名和值組成,因此不能像 list.Insert("test", 0)這樣寫,而是這樣 [mutableArray insertObject:@"test" atIndex:0]。

  當呼叫方法時給每個引數取個名字可以使程式碼非常清晰,但是不可避免的增加了程式碼量。總的來說這是件好事。另一方面,我認為讓方括號在目標物件之前開著,而不是在目標物件之後(像Java, C#, C++)是個錯誤。這使你更難確定你在一個表示式的位置,並且使一個簡單的序列變成了深層巢狀。擴充套件一個表示式是個反覆的事情,因為你必須回到行首新增 [。 當前輸入 ] 時XCode會嘗試猜測 [ 的相對位置,但是它老是搞錯以致變得更糟。

  幸運地是,Objective-c有些語法糖可以讓你在一般情況下襬脫巢狀。點記法 可以讓你重寫簡單的getter表示式,如[a value] 重寫為 a.value。setter, 陣列索引,字典查詢也有類似的語法糖。他們確實為減少冗長的程式碼創造了奇蹟。

  Objective-c 也有容器了,這個是關於這門語言我真的很喜歡的第一件事。你想要個字典?只需要輸入@{key:val,key2:val2,...}, 沒有自動包裝,因此你不得不在每一處新增@符號,像@{@"a":@1,@"b":@2,...}, 但是相比於人們過去不得不做的(大量地NSNumber numberWithInt:,以null結束的NSArray arrayWithObjectsfor:值,another key值,最後不幸中的萬幸NSDictionary dictionaryWithObjects:forKeys:)這只是很小的代價。

  我沒有預料到Object-C會有匿名函式的功能,但是它確實有(它們叫做塊)。塊語法因為缺乏推理型別語法,所以你不得不寫^(int x){return x*x;}用來替代 x=>x*x,但是確實足夠簡明可以被使用。

  另外一個對於Object-C語法來說的必要點就是字首使用“+”和“-”用於區分靜態方法和例項方法(錯誤的...資訊)。你將會很快的習慣它,雖然它的這些差別看起來有些滑稽。

  型別系統

  如果讓我說一句我恨Objective-C的地方,那一定就是型別系統了。它很難表示出很多有用的型別(例如沒有泛型)並且實際上它也不能檢查你的工作(甚至在執行時)。雖然它在感覺上很動態,但是我更喜歡靜態的強型別。

  一開始,Objective-C沒有null(但是C語言有,雖然你不常用)。替代的,Objective-C有nil,它很像null除了當你不小心在程式裡使用它的時候它不會使程式停止。發給nil的訊息不會丟擲異常,它僅僅只返回了一個nil並且實際上不作任何的評估。

  我是真的真的不喜歡nil. 例如,假設你寫了像建構函式的方法,它有一個叫SuperImportantSecurityChecker的引數。
你堅持引數不是nil,因為對不合邏輯的不被執行進行安全檢查是很糟糕的。
你也可以寫一個測試,故意導致安全失敗,並且檢查下確實是失敗了。你做的很好,因為你忘記了用SuperImportantSecurityChecker引數的值初始化SuperImportantSecurityChecker了。像這樣的錯誤會幾年都不會被發現,這多虧了nil。

  另一個例子,當程式已經出錯了,但是會繼續執行,令人吃驚的竟然是缺少執行時型別檢查。例如,如果你寫NSMutableArray* v = [NSArray array],Objective-C會高興的將你的可變陣列的指標指向一個不可變的陣列(你以前甚至沒有看到編譯報警,因為NSArray.array返回id, 現在它返回 instancetype)。當你嘗試去呼叫給“可變”陣列新增物件的方法時,程式會崩潰,錯誤提示為“selector not found”。這個並不像不可期的nil那樣糟糕,nil會悄悄地丟棄要新增的項而不是崩潰,但是查詢這些錯誤是很惱人的。

  當在塊上工作時缺少執行時檢查是非常噁心的,因為語言允許你過度自由的指定輸入型別。這種寫法看起來多麼誘人,[array enumerateObjectsUsingBlock:^(NSNumber* obj, NSUInteger index, BOOL *stop) { ... }];,根據你的期望,thatobjshould通常是一個數字而不是generalid,但是有時候你會搞砸它並且不得不追蹤問題所在。我已經採取了在大多數塊開始前加一行strewingassert([obj isKindOfClass:[WhateverType class]])來先發制人的捕捉這些錯誤。

  另一個值得注意的Objective-C會默默捨棄的是,如果你忘記了特定的編譯選項。你或許已經知道不指定“- Objc"會導致分類方法在執行時不被接受,儘管不會在編譯時被發現,但是你知道不包含-fobjc-arc-exceptions”會導致有異常丟擲時ARC不能正確的進行清理嗎?根據約定 強烈支援你不要捕獲異常是合理的,並且速度好處顯然不小,但是,蘋果讓他們的語言的預設執行不正確的行為著實讓我受寵若驚。

  基本上,我時常感覺Objective-C的型別系統是在挑戰我。我也嘗試寫一個安全的voip應用來保護你免受你的壓迫性的政府,但是我在用的語言實際上被設計成升級微不足道的錯誤來折中,而不是立馬崩潰。我覺得,我將從不會犯低階錯誤,因此沒有人會死,對嗎?這是完美的。或者有些人受虐待。。。我也許也會檢查它十多次。。。

  XCode

  語言的開發環境,是很影響你對該語言的感受的。開發Objective-C程式可以選擇蘋果公司的XCode工具,我個人比較習慣於使用XCode。此外還可以選擇JetBrain的AppCode開發環境來開發,雖然這個工具是收費的,但說實話我不是很喜歡,沒用多久就丟棄了。

  如果我給XCode評個分的話,滿分5分我給3分,算是中規中矩吧。XCode有許多細節做得很棒,比如, 這個fantastic  ,它會高亮顯示匹配的花括號(閃爍黃色高亮),還有方法自動補全之後,有行內氣泡提示,這也非常好用。不過也有許多不足之處。

  奇怪的是,我主要抱怨的是專案檔案的格式。我發誓,它設計就是為了引起合併衝突。我不知道是否UUID與各個條目、重複資訊,或者每行多種資訊相關。。。但我不記得最後一次我合併,而沒有手動修復&*(&ing專案檔案。

  有趣的是,對專案經常性的崩潰有些本末倒置。修復專案最快的方法是恢復它,然後通過把對應的資料夾拖進專案中重建組,來修復包含原始碼的組(這麼做是有效果的,因為原始碼檔案合併是正常的)。這個規律的重建操作保持你的組與檔案系統同步,因此你的專案不會在像github一樣的外部環境中看起來一團糟。多麼“方便”啊。

  我使用XCode的下一個問題是自動補全。它並不擅長這個。特別是當你在上面已經有了寫了一半的程式碼,在你編輯時總是可以保證看到部分結果。這會誘使你認為基本的方法不存在,因為你正在盯著一個不包括它們的補全列表。XCode自動補全的另一個情況是當你使用點語法時明顯不好:它基本沒工作過。輸入一個空格,你得到很多的結果,包括讀取器,但對應輸入一個點,你將可能看不到任何結果。令人沮喪。

   我對XCode的最後一個抱怨,也是值得提一下的,是有限的重構功能。在重新命名變數和一些其他東西時,會受限制,而且也沒什麼效果。這真的很慢(我見過花費幾分鐘的情況),另一半情況就是XCode崩潰。老實說,如果你想要做重構的話,你應該僅僅安裝AppCode。要麼用那個,要麼習慣忍受find+replace。

 總結

  Objective-C還行。它有很多很好的語法和好用的編輯器,但是(從一些喜歡靜態型別人們的角度來說)型別系統留下了許多可以改進的地方。

  至少,這是我個人的體會。

  原文地址:http://twistedoakstudios.com/blog/Post8237_a-years-worth-of-opinions-about-objective-c

相關文章