無線客戶端框架設(shè)計(jì)(4):自定義生命周期的設(shè)計(jì)(iOS篇)
首先要確定一點(diǎn),我們的App,要基于XIB文件進(jìn)行編程,而不是在每個(gè)相應(yīng)的ViewController里面去手動(dòng)創(chuàng)建頁面的每個(gè)控件。這樣做 的好處是,將頁面布局與業(yè)務(wù)邏輯徹底隔離。于是我們可以把xib的繪制工作交給美工人員,而iOS程序員,主要關(guān)心的是業(yè)務(wù)邏輯。
有人會(huì)懷疑過多的xib會(huì)導(dǎo)致App體積變大,我曾經(jīng)有專門看過ipa文件解壓后的文件列表,我發(fā)現(xiàn)每個(gè)xib也就2k大小,而一個(gè)App最多也就 七八十個(gè)xib,那么就是說共計(jì)150k大小的樣子,由于是xml文本文件,所以壓縮后更小。而相比較下,占用App體積最多的,往往是開機(jī)畫面圖,引導(dǎo) 圖這些東西,如果真的想要App瘦身,應(yīng)該在圖片上進(jìn)行優(yōu)化,而不是不使用xib直接布局。
另一個(gè)需要明確的是,在一開始創(chuàng)建ViewController的時(shí)候,不要同時(shí)創(chuàng)建xib文件,因?yàn)檫@樣子的話,就在xib中把 ViewController和xib進(jìn)行關(guān)聯(lián)了,而我們要做的是解耦,這顯然不合理。所以正確的流程是,分開創(chuàng)建ViewController和 xib,不要進(jìn)行管理。在ViewController的初始化中,加載xib文件,如下所示:
接下來要做的事情,有時(shí)候連我都覺得匪夷所思。我們先來看一段代碼:
- #import "APageViewController.h"
- @interface APageViewController ()
- @end
- @implementation APageViewController
- - (void)loadView
- {
- [super loadView];
- // Do any additional setup after loading the view.
- //1.從xib中獲取View
- NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil];
- self.view = list.lastObject;
- UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100];
- nameLabel.text = @"";
- UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200];
- ageLabel.text = @"";
- UIButton* getInfoButton = (UIButton*)[self.view viewWithTag: 300];
- [getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside];
- UIButton* clearInfoButton = (UIButton*)[self.view viewWithTag: 400];
- [clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside];
- }
- - (void) getInfo {
- UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100];
- nameLabel.text = @"包小強(qiáng)";
- UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200];
- ageLabel.text = @"31.6";
- }
- - (void) clearInfo {
- UILabel* nameLabel = (UILabel*)[self.view viewWithTag: 100];
- nameLabel.text = @"";
- UILabel* ageLabel = (UILabel*)[self.view viewWithTag: 200];
- ageLabel.text = @"";
- }
- - (void)dealloc {
- [super dealloc];
- }
- @end
上面的代碼,是再普通不過的一段代碼,讀取一個(gè)xib,獲取到View的句柄,初始化其中的每個(gè)控件,為按鈕掛上點(diǎn)擊后的方法事件,使得按鈕變紅。 內(nèi)部還有個(gè)計(jì)數(shù)器變量,每次點(diǎn)擊按鈕都會(huì)加1。巧的是,恰好還要偵聽一個(gè)通知(Notification)。最后,調(diào)用API。
我們發(fā)現(xiàn),有2個(gè)問題:
1)在willDidLoad中做了太多的事情,又是初始化變量,又是初始化控件,又是給按鈕掛事件,注冊(cè)通知,還要調(diào)用API。
2)每次使用控件時(shí),都要根據(jù)在xib中指定的tag重新獲取,而iOS中的控件tag值,只能是整數(shù)。
我們的解決方案是,既然頁面每次加載都會(huì)調(diào)用loadView和viewDidLoad方法,每次銷毀都會(huì)調(diào)用dealloc方法,那么干脆就在基類BaseViewController重寫了這幾個(gè)方法,于是現(xiàn)在頁面的生命周期如下所示:
相應(yīng)的基類代碼請(qǐng)參見本章的源碼。
我們?cè)诿總€(gè)頁面都會(huì)重寫createFields、loadData這些方法,每個(gè)方法的意義如下:
1)createFields和destroyFields: 創(chuàng)建/銷毀頁面級(jí)變量的地方。
2)createViews和destroyViews: 創(chuàng)建/銷毀頁面內(nèi)控件的地方。
3)createEvents和destroyEvents: 創(chuàng)建/銷毀頁面內(nèi)事件、通知的地方。
4)loadData: 如果頁面加載過程需要調(diào)用MobileAPI,則寫在這個(gè)地方。
我們?cè)诔绦蚶锇汛a分門別類寫在各自的地方,易于管理(避免了經(jīng)常會(huì)聲明了變量而忘記銷毀的問題)。
于是剛才的代碼文件,我們將其重構(gòu)為:
- #import "APageViewController.h"
- @interface APageViewController () {
- UILabel* nameLabel;
- UILabel* ageLabel;
- UIButton* getInfoButton;
- UIButton* clearInfoButton;
- }
- @end
- @implementation APageViewController
- - (void)createFields {
- }
- - (void)destroyFields {
- }
- - (void)createViews {
- //1.從xib中獲取View
- NSArray* list = [[NSBundle mainBundle] loadNibNamed: @"APageView" owner: self options: nil];
- self.view = list.lastObject;
- nameLabel = (UILabel*)[self.view viewWithTag: 100];
- nameLabel.text = @"";
- ageLabel = (UILabel*)[self.view viewWithTag: 200];
- ageLabel.text = @"";
- getInfoButton = (UIButton*)[self.view viewWithTag: 300];
- clearInfoButton = (UIButton*)[self.view viewWithTag: 400];
- }
- - (void)destroyViews {
- }
- - (void)createEvents {
- [getInfoButton addTarget: self action: @selector(getInfo) forControlEvents:UIControlEventTouchUpInside];
- [clearInfoButton addTarget: self action: @selector(clearInfo) forControlEvents:UIControlEventTouchUpInside];
- }
- - (void)destroyEvents {
- }
- - (void)loadData {
- //在這里調(diào)用API,對(duì)于多個(gè)API的調(diào)用,參加后續(xù)章節(jié)
- }
- - (void) getInfo {
- nameLabel.text = @"包小強(qiáng)";
- ageLabel.text = @"31.6";
- }
- - (void) clearInfo {
- nameLabel.text = @"";
- ageLabel.text = @"";
- }
- @end
以上的代碼重構(gòu),要遵守幾個(gè)規(guī)則:
1)在createFields方法中接收從上一個(gè)頁面?zhèn)鬟f過來的參數(shù)
2)在createFields方法中初始化變量
3)將要操作的控件,都在ViewController中作為類級(jí)別的變量來聲明
3)在createViews方法中,加載xib文件,并通過Tag給控件一次性賦值
4)在createEvent方法中,為控件掛上事件方法,比如按鈕的點(diǎn)擊
5)如果有NotificationCenter,統(tǒng)一在createEvent方法中addObserver,在destroyEvent方法中removeObserver。
6)在DestroyFields方法中,釋放/銷毀所有引用型變量。
7)在DestroyViews方法中,釋放/銷毀所有控件。
所有的ViewController都這么寫,整個(gè)App整齊劃一。尤其是將一個(gè)頁面的所有控件一次性都從xib中根據(jù)tag值取出來,雖然浪費(fèi)了一些內(nèi)存,但是可以隨時(shí)隨地直接使用。
將聲明一個(gè)按鈕和為按鈕添加一個(gè)點(diǎn)擊事件方案分開在2個(gè)方法內(nèi)寫,一開始你會(huì)非常不習(xí)慣,但是當(dāng)控件多了、事件多了的時(shí)候,是一目了然的。記住,我們?cè)谧龅氖瞧髽I(yè)級(jí)App開發(fā),而不是小型App。
看到最后,熟悉網(wǎng)站端編程的人笑了,沒錯(cuò),這種新的生命周期,就是從javascript中借鑒來的。js是一門弱語言,所以需要自定義生命周期并按部就班在不同的方法中寫不同的方法,生命周期的重新定義,或者說是擴(kuò)展,只是js代碼框架中的一個(gè)小部分。
本章代碼下載:
YoungHeart-Chapter-04-1.zip (重構(gòu)前)
YoungHeart-Chapter-04-2.zip (重構(gòu)后)