作為開發團隊的負責人,要兼顧的事務已不只是具體某個功能,某個介面的實現,而是如何在現有代碼的基礎上做漸進式的改進,創造出比較合適規範和框架,使得整個團隊更快地完成任務。關於iOS App開發,有些心得可以在此分享:
合適的人
明確一點,合適的人是指純技術團隊的建設。一支戰鬥力再強的技術團隊,面對一個朝三暮四,分分鐘推翻自己原有想法的項目經理,再好的戲也唱不出來。花幾個月加班趕工,還沒發佈,又直接推翻重做,這時候你就得上網理財看看自己財政狀況,明天努力找工作吧。IT界有句名言:電腦相關的所有問題都可以通過增加一個額外的抽象層來解決。但是軟件發展卻不是這樣:增加層(人手)在一定程度上可以加快開發進度,當過了某個限度後,其效果就不是那麼明顯,甚至會人多手腳亂。
對於一個專案而言需要的往往不是更多的成員,而是適量的合適成員。每一個人因為不同的資歷背景、工作經歷(技術選型,學習經歷,專案管理),對程式開發都會有不同的理解和思維。反應在業務上就是各種各樣的代碼風格。舉例來說:有些人恨不得把所有單一功能都一一獨立出來封裝成類,而有些人卻喜歡直腸子的寫上千行代碼。大部分情況下很多人都是傾向前者,但It depends……
一群理念相去甚遠的人在一起工作是件異常痛苦的事:相當一部分的時間會浪費在解釋,爭論和排遣由此帶來的沮喪和憤怒上。古人語:道不同,不相為謀。但到了真正的工作中卻不能如此隨性,缺乏足夠動力的老人,能力出眾的技術骨幹,幹勁十足卻缺乏經驗的新人都需要互相體諒,學習和磨合。所以大部分的小企業內,技術團隊因為理念相近,往往效率會較高,而大公司內的開發小組卻永遠無法達到那樣的效率,更需要相應的規範和程式框架。
少數人在沒有任何外界壓力的情況下仍會不斷總結學習進步(主動學習型),而其餘的人要麼沒有任何意願,關心的只是完成任務和拿到工資而已,要麼想要進步而不得法。而你的團隊不可能全由主動學習型的成員組成,這時候規範和程式框架的引入才能夠讓各種類型的人更好的合作。
合適的規範
需要合適的規範包括代碼規範、程式規範、流程規範等等,以此來減少意外的出現。但實際執行中卻會碰到各種問題,例如怎麼鑒別哪些規範是需要強制執行,那些規範是推薦執行。規範的強制執行帶來的是代碼的可讀性提升和差異減少,壞處是規定太死板,限制了有個人想法的開發人員,減少創新的可能性。
而如果大部分的規範設定為推薦執行,在沒有良好的監管下,規範容易被忽視。 網上有很多關於ObjC的代碼規範,比如蘋果自家的規範和《Google Objective-C Style Guide》等,可惜這些規範一般只有兩種分級:推薦和不推薦,並沒有實際的參考價值。建議管理人員把代碼規範分成五個等級,強制要求,強烈推薦,一般推薦,可參考和無須理會,以下僅舉部分例子加以說明:
強烈要求:
符合蘋果規範的命名方式。 類名開頭大寫,方法和變數名以駝峰法命名。因為蘋果系統類庫和絕大多數的協力廠商開源碼都是如此。
強烈推薦
類名使用至少三個字元做首碼,內部方法使用兩個底線做首碼。因為蘋果宣稱保留所有兩位元字元首碼的使用權,同時其內部方法命名以一個底線做首碼。
推薦
合理的代碼結構如下的工程目錄結構
可參考
無論使用K&R Style還是Allman Style都是可接受的範圍,但是在一個檔內要保持統一形式。
小類,小方法,統一的函數返回。小類,小方法可以保證他人閱讀時更方便地關注類邏輯,而不是具體細節,而統一的函數返回可以減少意外錯誤和降低錯誤排查的難度,而保證代碼的簡短,為整個工程創建Workspace按照權責分門別類存放資源檔:每種類型的資源存放於獨立的目錄下:圖片,聲音,設定檔等等。而圖片又可以按照類型分別存放在不同的子目錄下:全域類型,背景圖,logo,登錄等等。
合適的框架
一個合適的框架不是銀彈,有了框架之後,工程不代表能正確地進行。框架能夠做到的僅僅是降低通用問題的複雜度和減少發生錯誤的可能性。個人認為一個良好iOS App框架應該是有如下特點:
定義清晰的層次結構。 界面層(Presentation layer),負責管理UI和UIViewController 邏輯層(Business/Service Layer),負責邏輯資料的定義和轉發,起到承上啟下的作用。 資料訪問層(Data Access Layer),負責具體API構造,網路請求,資料持久化等。
各層根據業務邏輯的複雜性內部又會使用單層或者多層結構。以資料訪問層為例,一般又可以細分為網路層、持久化層。界面層都是直接使用邏輯層提供的Model作展示,但是某些情況下往往需要不同的Model有相同的介面展示,這就需要增加額外的ViewModel層用於結合界面層和邏輯Model。
橫向上,各模組互相獨立,僅通過有限的幾個介面進行通訊。最理想的狀態是除核心模組外,其他模組都是可拔插。縱向上,各層次間依賴關係清晰,基本不出現逆向依賴的情況。
橫向模組一般依賴於業務需求,常被定義成各種Service或Manager。一種好的做法是有個統一的Service管理器負責相應Serivce的載入,卸載,監聽和分發App級別的通知給相應Service,如前後臺切換,收到記憶體警告等。這樣做一方面容易實現上面說的模組的可轉換,另一方面也保證了公用特性處理的一致性。在這方面微信就做得不錯,基本所有的模組都是從MMService繼承而來,由MMServiceCenter進行管理。當然從dump出來的標頭檔也可以發現一些管理上的紊亂,比如一些ViewController都是繼承自MMService。
縱向的層次劃分基本各個App不會有太大區別,一般可以分為三個層次:
遵守SOLID原則和慎用各種設計模式。這是個老生常談的話題了,並不是iOS開發獨有,展開講可以講上幾天幾夜,不贅述。
定義自己的UI基類:UIView,UIViewController,UITableviewCell。這一點的好處不言而喻,所有的子View,Controller,Cell都能夠很方便的繼承基類的共有的行為,樣式。但也會引進很大的管理風險:組內成員總會經不起誘惑往基類塞各種並不普適的特性,引起基類權責的無限膨脹。大基類不僅增加組內成員對代碼的理解難度,同時也增加出現問題時的排查難度。從這方面講,微信的UIViewController基類設計就極端失敗:MMUIViewController。
提供方便好用的工具類
一些好用的工具類往往會成為框架重要的有機組成部分,方便快捷地解決局部問題,同時又不引入過多的複雜度。NSTimer的retain cycle是個很容易掉去的坑,那麼提供一個基於Block或者weak delegate的NSTimer的封裝就是不錯的選擇。使用KVO容易發生add和remove的不配對調用,那麼就引入THObserversAndBinders或者FB的KVOContorller。某些核心模組需要被多個模組依賴時,引入類似XMPP的GCDMulticastDelegate就能夠方便地進行解耦。
建立良好的範例
在前幾年使用C++的那段暗無天日的日子裡,我常想的一個問題是:如何在API層面去限制和規避一些錯誤,比如向執行排程裡面扔的task必須是堆上分配的物件,那麼如何去強制傳入的指標指向的是堆地址而不是棧地址呢?這種傻問題大部分情況下是無解的,而現在我更相信破窗理論所提供的可能性:做好示範,接下來的一切都會水到渠成。
iOS App開發的那些事
https://www.facebook.com/hkitblog