首先要確定一點,我們的App,要基于XIB文件進行編程,而不是在每個相應的ViewController里面去手動創建頁面的每個控件。這樣做的好處是,將頁面布局與業務邏輯徹底隔離。于是我們可以把xib的繪制工作交給美工人員,而iOS程序員,主要關心的是業務邏輯。
有人會懷疑過多的xib會導致App體積變大,我曾經有專門看過ipa文件解壓后的文件列表,我發現每個xib也就2k大小,而一個App最多也就七八十個xib,那么就是說共計150k大小的樣子,由于是xml文本文件,所以壓縮后更小。而相比較下,占用App體積最多的,往往是開機畫面圖,引導圖這些東西,如果真的想要App瘦身,應該在圖片上進行優化,而不是不使用xib直接布局。
另一個需要明確的是,在一開始創建ViewController的時候,不要同時創建xib文件,因為這樣子的話,就在xib中把ViewController和xib進行關聯了,而我們要做的是解耦,這顯然不合理。所以正確的流程是,分開創建ViewController和xib,不要進行管理。在ViewController的初始化中,加載xib文件,如下所示:
接下來要做的事情,有時候連我都覺得匪夷所思。我們先來看一段代碼:
1 #import "APageViewController.h" 2 3 @interface APageViewController () 4 5 @end 6 7 @implementation APageViewController 8 9 - (void)loadView 10 { 11 [super loadView]; 12 // Do any additional setup after loading the view. 13 14 //1.從xib中獲取View 15 NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil]; 16 self.view = list.lastObject; 17 18 UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100]; 19 nameLabel.text = @""; 20 21 UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200]; 22 ageLabel.text = @""; 23 24 UIButton* getInfoButton = (UIButton*)[self.view viewWithTag: 300]; 25 [getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside]; 26 27 UIButton* clearInfoButton = (UIButton*)[self.view viewWithTag: 400]; 28 [clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside]; 29 } 30 31 - (void) getInfo { 32 UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100]; 33 nameLabel.text = @"包小強"; 34 35 UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200]; 36 ageLabel.text = @"31.6"; 37 } 38 39 - (void) clearInfo { 40 UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100]; 41 nameLabel.text = @""; 42 43 UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200]; 44 ageLabel.text = @""; 45 } 46 47 - (void)dealloc { 48 [super dealloc]; 49 } 50 51 @end
上面的代碼,是再普通不過的一段代碼,讀取一個xib,獲取到View的句柄,初始化其中的每個控件,為按鈕掛上點擊后的方法事件,使得按鈕變紅。內部還有個計數器變量,每次點擊按鈕都會加1。巧的是,恰好還要偵聽一個通知(Notification)。最后,調用API。
我們發現,有2個問題:
1)在willDidLoad中做了太多的事情,又是初始化變量,又是初始化控件,又是給按鈕掛事件,注冊通知,還要調用API。
2)每次使用控件時,都要根據在xib中指定的tag重新獲取,而iOS中的控件tag值,只能是整數。
我們的解決方案是,既然頁面每次加載都會調用loadView和viewDidLoad方法,每次銷毀都會調用dealloc方法,那么干脆就在基類BaseViewController重寫了這幾個方法,于是現在頁面的生命周期如下所示:
相應的基類代碼請參見本章的源碼。
我們在每個頁面都會重寫createFields、loadData這些方法,每個方法的意義如下:
1)createFields和destroyFields: 創建/銷毀頁面級變量的地方。
2)createViews和destroyViews: 創建/銷毀頁面內控件的地方。
3)createEvents和destroyEvents: 創建/銷毀頁面內事件、通知的地方。
4)loadData: 如果頁面加載過程需要調用MobileAPI,則寫在這個地方。
我們在程序里把代碼分門別類寫在各自的地方,易于管理(避免了經常會聲明了變量而忘記銷毀的問題)。
于是剛才的代碼文件,我們將其重構為:
1 #import "APageViewController.h" 2 3 @interface APageViewController () { 4 UILabel* nameLabel; 5 UILabel* ageLabel; 6 UIButton* getInfoButton; 7 UIButton* clearInfoButton; 8 } 9 10 @end 11 12 @implementation APageViewController 13 14 - (void)createFields { 15 16 } 17 18 - (void)destroyFields { 19 20 } 21 22 - (void)createViews { 23 //1.從xib中獲取View 24 NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil]; 25 self.view = list.lastObject; 26 27 nameLabel = (UILabel*)[self.view viewWithTag: 100]; 28 nameLabel.text = @""; 29 30 ageLabel = (UILabel*)[self.view viewWithTag: 200]; 31 ageLabel.text = @""; 32 33 getInfoButton = (UIButton*)[self.view viewWithTag: 300]; 34 clearInfoButton = (UIButton*)[self.view viewWithTag: 400]; 35 } 36 37 - (void)destroyViews { 38 39 } 40 41 - (void)createEvents { 42 [getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside]; 43 [clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside]; 44 45 } 46 47 - (void)destroyEvents { 48 49 } 50 51 - (void)loadData { 52 //在這里調用API,對于多個API的調用,參加后續章節 53 } 54 55 - (void) getInfo { 56 nameLabel.text = @"包小強"; 57 ageLabel.text = @"31.6"; 58 } 59 60 - (void) clearInfo { 61 nameLabel.text = @""; 62 ageLabel.text = @""; 63 } 64 65 @end
以上的代碼重構,要遵守幾個規則:
1)在createFields方法中接收從上一個頁面傳遞過來的參數
2)在createFields方法中初始化變量
3)將要操作的控件,都在ViewController中作為類級別的變量來聲明
3)在createViews方法中,加載xib文件,并通過Tag給控件一次性賦值
4)在createEvent方法中,為控件掛上事件方法,比如按鈕的點擊
5)如果有NotificationCenter,統一在createEvent方法中addObserver,在destroyEvent方法中removeObserver。
6)在DestroyFields方法中,釋放/銷毀所有引用型變量。
7)在DestroyViews方法中,釋放/銷毀所有控件。
所有的ViewController都這么寫,整個App整齊劃一。尤其是將一個頁面的所有控件一次性都從xib中根據tag值取出來,雖然浪費了一些內存,但是可以隨時隨地直接使用。
將聲明一個按鈕和為按鈕添加一個點擊事件方案分開在2個方法內寫,一開始你會非常不習慣,但是當控件多了、事件多了的時候,是一目了然的。記住,我們在做的是企業級App開發,而不是小型App。
看到最后,熟悉網站端編程的人笑了,沒錯,這種新的生命周期,就是從javascript中借鑒來的。js是一門弱語言,所以需要自定義生命周期并按部就班在不同的方法中寫不同的方法,生命周期的重新定義,或者說是擴展,只是js代碼框架中的一個小部分。
本章代碼下載:
YoungHeart-Chapter-04-1.zip (重構前)
YoungHeart-Chapter-04-2.zip (重構后)
文章列表