iOS專案重構週記(一)

weixin_33860722發表於2015-07-05

最近開始做公司的iOS專案重構,現準備每週做一次彙總,把重構過程中遇到的問題和解決方案記錄下來,做一個記錄和分享。

1.檔案目錄結構

我們在Xcode中使用“new group”建立一個新的目錄時,對應的檔案系統中並不會相應的建立一個實體資料夾,只是在Xcode中建立一個便於管理的虛擬資料夾。這樣就導致新增的所有檔案最終都放在檔案系統的同一目錄下,這裡面可能會包含.h檔案、.m檔案、nib檔案,圖片等等一系列的資原始檔。當專案最終變的比較龐大時,眾多的檔案將變的極難管理。而且,當我們使用SVN或Git來做版本控制時,一旦產生工程檔案衝突,重新新增檔案也很難定位,並且容易遺漏檔案。

所以我們應該使用實體資料夾來構建專案中的Group,另外當專案比較龐大時,Group的劃分也是需要斟酌的一項。好的結構劃分能夠幫助我們快速定位程式碼,有助於理解整個專案的架構和邏輯。所以我建議基於以下幾條來區分目錄結構。

  • 公共部分和各功能模組的區分
    公共部分和各功能模組應該區分開來,公共模組一般包含公共模型、方法、檢視、第三方庫。我們寫的任何可被其他功能模組呼叫的元件都應該包含到公共目錄下。

  • 資源型別的區分
    所有的圖片、資料庫檔案、bundle、plist等等資原始檔都應該統一包含到資源目錄下。

  • MVC的區分
    各功能模組都可按MVC來區分,檢視模型控制器的區分可以幫助自己和他人更快的定位程式碼。

另外說一下新增資料夾到工程中時,“Create groups”和“Create folder references”的區別。

img

Create group類似於我們“new group”時建立的組,其中包含的檔案會自動新增到Compile Sources中,Create folder references只會引用資料夾,資料夾裡面的東西都會直接拷貝到bundle包,不參與編譯。

2.註釋

註釋也是重構中的一部分,好的註釋能夠極大程度上幫助自己和他人理解程式碼。我相信對註釋負責的人,也從側面證明是一個靠譜的人。只是這裡要注意註釋的格式。

這裡推薦一個喵神寫的自動註釋工具:VVDocumentor
這是一個Xcode外掛,只需要在要寫文件的程式碼上面連打三個斜槓,就能自動提取引數等生成規範的Javadoc格式文件註釋,下載編譯一下,然後重啟Xcode就可以使用了。

img

使用這種方式的註釋,只要按住option鍵+滑鼠左鍵,是可以在呼叫時直接檢視註釋內容的:
img

3.手寫程式碼 or Xib?

關於這個問題相信很多同學都有困惑,國內iOS界的大神唐巧和喵神對這個問題也都有自己的見解,大家可以移步到他們的部落格看看:
唐巧:http://blog.devtang.com/blog/2015/03/22/ios-dev-controversy-2/
喵神:http://onevcat.com/2013/12/code-vs-xib-vs-storyboard/
借用唐巧的幾句話:

  • 對於複雜的、動態生成的介面,建議使用手工編寫介面。
  • 對於需要統一風格的按鈕或UI控制元件,建議使用手工用程式碼來構造。方便之後的修改和複用。
  • 對於需要有繼承或組合關係的 UIView 類或 UIViewController 類,建議用程式碼手工編寫介面。
  • 對於那些簡單的、靜態的、非核心功能介面,可以考慮使用 xib 或 storyboard 來完成。

4.多用型別常量,少用#define

一個龐大的專案中,常常使用了大量的巨集定義。巨集定義的初衷之一是提高了程式的可讀性,同時也方便進行修改。可是過度的巨集定義往往違背了它的初衷。

例如

#define ANIMATION_DURATION 0.3 

我們並不能很直觀的理解它其中的時間含義,而

static const NSTimeInterval kAnimationDuration = 0.3;

就很好的描述了常量的含義。

此外,為什麼要加一個static和const來同時命名?因為static意味著該變數僅在定義此變數的編譯單元中可見。編譯器每收到一個編譯單元,就會相應的輸出一份目標檔案(object file即.o檔案)。假如我們不宣告static,編譯器就會為它建立一個“外部符號”。此時如果另一個編譯單元也宣告瞭一個同名變數,那麼編譯器就會丟擲一條錯誤訊息。事實上,如果同時用static和const命名,編譯器根本不會建立符號,而是會像#define預處理指令一樣,把所有遇到的變數都替換為常值。不過還是有一點區別的,用這種方式定義的常量是帶有型別資訊的。

不管是#define還是static const都不應該在標頭檔案裡宣告,因為常量名稱很有可能互相沖突,如果一定要這麼做的話,要加上字首,表明它輸入哪個類。

再延伸的說一下extern:
extern常用於NSNotification中等,供外部使用,extern常量放在“全域性符號表”中,以便可以在定義該常量的編譯單元之外使用。這也是不用使用#import引入其所在標頭檔案的原因,需注意,此類常量必須要定義,而且只能定義一次。

5.Reveal

對於龐大的專案,純程式碼構建的UI,使用Reveal來除錯介面的用處還是很大的。可以幫助我們更直觀的瞭解程式碼,快速定位UI細節。顏色,位置,大小,間距,和View之間的相對關係都可以一目瞭然。Reveal甚至比Xcode自帶的Interface Builder做的還要好。對於越獄的裝置,Reveal還可以用來分析其他應用程式的UI,實在是不可多得的利器。

img

Reveal載入的三個方法

載入方法1

下載Reveal之後開啟,在選單中的Help中可以找到整合到Xcode專案的方法,這裡不再贅述。

載入方法2

此方法可以在不改變工程設定的前提下載入Reveal
開啟終端,輸入

vim ~/.lldbinit

LLDB每次啟動的時候都會載入這個檔案。輸入:

command alias reveal_load_sim expr (void*)dlopen("/Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/libReveal.dylib", 0x2);

command alias reveal_load_dev expr (void*)dlopen([(NSString*)[(NSBundle*)[NSBundle mainBundle] pathForResource:@"libReveal" ofType:@"dylib"] cStringUsingEncoding:0x4], 0x2);

command alias reveal_start expr (void)[(NSNotificationCenter*)[NSNotificationCenter defaultCenter] postNotificationName:@"IBARevealRequestStart" object:nil];

command alias reveal_stop expr (void)[(NSNotificationCenter*)[NSNotificationCenter defaultCenter] postNotificationName:@"IBARevealRequestStop" object:nil];

然後輸入control+c,w q enter退出終端。
上述檔案建立了4個命令:

reveal_load_sim,reveal_load_dev, reveal_start 和 reveal_stop
  • reveal_load_sim 這個只在iOS模擬器上有效。它從Reveal的應用程式bundle中找到並載入libReveal.dylib(請確保你把Reveal安裝到了系統的Application資料夾,如果你換地方了,你修改上述的檔案)。

  • reveal_load_dev 這個命令在iOS裝置和模擬器上都有效。不過,它需要你在Build Phase中的的Copy Bundle Resources中加上libReveal.dylib,請確保沒有放到Link Binary With Libraries這個地方。

  • reveal_start 這個命令發出一個通知啟動Reveal Server。

  • reveal_stop 這個命令發出一個通知停止Reveal Server。

如果在模擬器下除錯,只需要在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法中加入一個斷點並且如圖所示編輯:

img

編譯執行即可。

在真機下,需在工程中匯入Reveal的動態庫,開啟Reveal,點選Help-Show Library in Finder,將libReveal.dylib檔案拖動到目標Xcode工程中,Xcode預設情況下錯誤地將libReveal.dylib設定到了”Link Binary With Libraries”下,我們需要進行一下調整,將其中”Link Binary With Libraries”中刪除,然後將其新增到“Copy Bundle Resources”下面。

img

之後用Reveal連線真機的方式和連線模擬器的方式類似,我們只需要把模擬器除錯下的斷點Action的內容從reveal_load_sim改成reveal_load_dev即可。

載入方法3

對於越獄的機器,可以用Reveal來”除錯“其它應用介面,什麼時候會有這種奇怪的需求呢?——當我們想學習別人是如何實現介面效果的時候。iOS裝置的目錄/Library/MobileSubstrate/DynamicLibraries下存放著所有在系統啟動時就需要載入的動態連結庫,所以我們只需要將Reveal的動態連結庫上傳到該目錄即可。

對於越獄的裝置,我們可以在安裝OpenSSH之後,用scp來上傳該檔案。具體步驟如下:
1.將libReveal.dylib 上傳到/Library/MobileSubstrate/DynamicLibraries
2.如果libReveal.dylib沒有執行許可權,用chmod +x libReveal.dylib命令,給其增加執行許可權
3.執行killall SpringBoard重啟桌面

另外需要注意的是,使用Reveal真機測試時手機和電腦應處於同一網路下。

相關文章