iOS開發

Jonrencxr發表於2015-11-17

iOS開發之路-菜鳥探索

我的部落格文件 2015.10.13 

畢業從事iOS工作一年多了,從畢業前自學iOS到工作,再到獨立開發了幾個專案,有一種感覺就是永遠都是嫌棄自己的程式碼,即使有時候做出來的功能並不差,可是程式碼能力真的不敢苟同。我也認真看了幾個優秀的開源專案,比如OSChina、AmericanEnglish等,專案架構、程式碼佈局、封裝整合等都做得很出色,用最少的程式碼、最方便易改的程式碼做出優秀的專案,這是極難的,沒有一定的經驗積累只是照搬硬抄也很難有所突破。但是,程式設計師,不就是要保持一顆永遠不畏懼勇往直前不停學習的屌絲心態嗎。下面,發點自己的平時的一些循循漸進的感悟,記錄碼工的程式碼點滴。

一、關於程式碼佈局:

程式碼佈局向來是一個巨大的難題,每個不同的優秀程式設計師都可能擁有自己的佈局方式,但是總體而言,肯定都是為了更加的整潔清晰和便於更新修改。記得剛工作自己做的專案,不知道如何分類程式碼,不知道如何整合封裝,所有的程式碼全部寫在viewDidLoad裡面,先按順序佈局頁面,然後請求資料重新賦值重新整理頁面,不同的程式碼用多個空格隔開,看起來還算整齊,可是,毫無章法。

有經驗的同事看了我的程式碼,然後偷偷的告訴我,程式碼千萬不要全部寫到一塊,要不然,太菜!然後,我學會了拆分各個功能不同的程式碼到各個void方法中,然後在viewDidLoad裡面按順序self呼叫。是不是感覺很低階?沒錯,我想這是每個程式設計師一開始都會遇到的問題。

本來以為方法按順序寫就行了,後來介面功能增多,方法也增多,尋找方法也變得困難,後來用#pragma mark進行了分類,初始化介面initSubviews,代理,點選事件,伺服器請求,其他方法,這樣,整個程式碼介面相對來說比較規範,便於查詢修改。但是主控器的程式碼多的時候甚至超過1000行,程式碼冗餘度過高,也不夠簡潔化,所以,我開始考慮減少主控器的程式碼。

    1. 從基礎控制元件下手。開始嘗試獨立的storyboard和靈活的XIB,UI佈局程式碼一下子減少了很多。但是考慮到純程式碼的複用性比較高,所以,開始利用公共方法建立常見的UI控制元件:

然後建立輸入框和按鈕時直接呼叫該方法:(需要呼叫標頭檔案#import "CXRKitTool.h"

 

2. 從複雜的view下手。又時候,我們發現有些view裡面控制元件十分多,寫在住控制器裡面難免混亂,或者說不夠簡單。


比如這樣一個簡單地顯示介面,要寫一個view,然後在view新增圖片,兩個標籤,又是不少程式碼,因此,考慮建立一個自定義TimerView,然後簡單的呼叫即可。

建立一個繼承於UIView的TimerView,然後初始化各個控制元件:


然後通過簡單的幾句程式碼呼叫就可以實現這個view了;


關於簡化程式碼還有很多特別優秀的方法,比如把每個功能模組都單獨整合封裝出來,使用第三方工具等,我還經驗欠缺,繼續學習。

 

二、關於常見的方法封裝:

比如,我們總是需要多處展示載入條,我們需要多處把時間戳轉化為時間格式的字串,需要多次把字典轉化為data,需要多處驗證手機號、郵箱和身份證號,需要多處顯示某個自定義彈出框等等,如果每次都重新寫一遍程式碼的話,只能說明自己太low了,一開始的我就是,但是誰叫我不願意做一名菜鳥呢,我就蒐集資料,然後自己總結,終於建立了自己第一個常見的類NSObject方法。

舉個例子,我想把時間戳轉化為時間格式的字串方法封裝起來,建一個類,叫MyEngine,在MyEngine.h檔案裡面宣告方法:

+ (NSString*)timestmpToDatetimeString:(NSString *)string;

然後在MyEngine.m檔案裡面寫明具體實現方法:

+ (NSString*)timestmpToDatetimeString:(NSString *)string

{

    NSString *dateStr = nil;

    if (!string) {

        dateStr = @"0000-00-0000:00:00";

    }else {

        NSDateFormatter *formatter =[[NSDateFormatter alloc] init];

        [formattersetDateFormat:@"yyyy-MM-dd HH:mm:ss"];

        NSTimeInterval time = [stringdoubleValue];

        NSDate *date = [NSDatedateWithTimeIntervalSince1970:time];

        NSLog(@"date:%@",[datedescription]);

        dateStr = [formatterstringFromDate:date];

    }

   

    return dateStr;

}

這樣,在專案裡面每次需要時間戳轉時間字串時就可以直接引用MyEngine.h檔案,然後測試:

NSString*timestmp = @"1447405653";

    NSString*dateTime= [MyEnginetimestmpToDatetimeString:timestmp];

   NSLog(@"time:%@", dateTime);

列印出來的結果就是time:2015-11-13 09:07:33,這樣,這個方法就變成了可重複使用的程式碼,並且減少了程式碼量。如果需要新增新增其他方法,同上,分別在.h檔案和.m檔案裡面分別定義方法和實現即可。當然,很多大牛不建議把各種大雜燴的處理方法放在一個類裡面,比如,如果是關於日期時間處理的方法全部放在一個類(比如DateEngine)裡面,處理載入框的方法全部放在類(比如Hud)裡面,處理伺服器請求的方法全部放在類(比如RequestClass)裡面,這樣分類就比較清楚,後者修改起來也變得更加清晰,更加方便。有一些大牛程式碼封裝的極其厲害,其他程式設計師檢視其程式碼的時候通常要看很多遍才能找到他的邏輯,或者根本無法理解,那是因為程式碼整合封裝過多過亂,然後有沒有明確地標記和分類,才導致這個問題,一般不建議這麼做,最好做好明確地分類。

三、關於傳值:

大家有沒有發現,在專案裡面,因為功能程式碼的整合和控制器之間的各種跳轉,某些資訊的傳遞總是不可避免,傳值的方法網上分類統計的也很清楚了,直接傳值、代理、通知、block和本地存取等方式,我就不復制貼上了。我想談談我自己的一步一步的進階學習和感悟。

記得第一次頁面之間的傳值,我第一次接觸了代理方法delegate,一開始不知道怎麼回事,只是強制的記住它的實現方法,介面A->B,如果A要把某個字串productId傳遞給B,那麼在A裡面B *b = [[B alloc] init]; b. productId = a. productId;就可以直接傳遞,如果B需要把值傳遞到A,那麼不能直接傳值,這時候可以使用代理方法,在B中宣告代理方法並在合適的時候觸發,A如果引入了B的代理方法,在A中便會自動呼叫B的代理方法。

這個方法貫穿了我整個第一個專案,可是又來了新的需求,沒有頁面直接跳轉,我想在介面C獲取A的productId,該怎麼辦呢?當時還很急躁,又想不到其它方法,老闆又在催,正好當時正在使用一個本地儲存使用者登入資料的方法NSUserDefaults,然後很高興的把productId存到了NSUserDefaults(在A裡面),

   NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults];

    [userDefaults setObject: productId forKey:@"productId"];

    [userDefaults synchronize];

然後在C裡面獲取:

NSUserDefaults*userDefaults = [NSUserDefaults standardUserDefaults];

    NSString *productId = [userDefaultsobjectForKey:@"productId"];

方法雖然笨拙,但是確實解決了當時的問題。

那麼問題又來了,專案使用者登入用到了NSUserDefaults,但是退出登入的時候需要清空登入資訊,如果如果刪除不慎,也會不小心把productId也給刪了,這如果本地儲存的資料更多,就會更加的繁雜。寶寶一下子又不開心了,怎麼辦?怎麼辦??還是看看別人是怎麼做的吧,開源專案,用到了通知NSNotification和block,都能夠解決不同需求的傳值問題。例如,介面A要實時獲取彈出的登入介面C中的登入成功返回的資訊,可以在C中傳送通知:[[NSNotificationCenter defaultCenter] postNotificationName:@”NotificationSuccess”object:@(YES)];

然後在A中

-(void)viewWillAppear:(BOOL)animated {

    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(doSomething) name:@"NotificationSuccess " object:nil];

}

 

- (void)viewWillDisappear:(BOOL)animated{

    [[NSNotificationCenter defaultCenter]removeObserver:self name:@" NotificationSuccess " object:nil];

}

然後就可以在doSomething裡面做登入成功後想要做的任何事,必須注意的是通知不會自動消失,會一直保留通知的功能,會造成消耗記憶體等弊端,所以獲取通知過後一定要及時移除通知,移除方法最好放在viewWillDisappear裡面。

最後,重點介紹一下block,因為用過的都知道,block用起來最靈活方便,依然用A和登入介面C,在C中,.h檔案裡面,定義block方法,

typedefvoid(^LoginBlock)(BOOL logined);

 

@property (copy,nonatomic) LoginBlock block;

-      (void)loginSuccess:(LoginBlock)block;//用於登入成功的回撥

然後在.m檔案,設定登入成功方法:

-(void)loginSuccess:(LoginBlock)block {

    self.block = block;

}

然後在登入成功的方法裡面呼叫self.block(YES);這樣就把登入成功的值放在了block裡面了,記住block裡面幾乎可以放任何引數,多個引數,這裡只是簡單的傳遞一個YES。然後重點來了,在A介面需要跳轉到C的某個地方:

LoginViewController*viewc = [[LoginViewController alloc] init];

            [viewc loginSuccess:^(BOOL logined){

                if (logined) {

                    [self doSomething];

                }

            }];

            UINavigationController *nav =[[UINavigationController alloc] initWithRootViewController:viewc];

[self.tabBarController presentViewController:nav animated:YEScompletion:nil];

看到了,在A裡面只需要呼叫block方法就可以直接獲取C實時返回資料處理當前A的介面操作,是不是比代理方法還要清晰簡單?當然了,block也有一些弊端,我也沒有深入研究,但是你發現了嗎,蘋果官方程式碼基本都是用的block!可見它的優點不是一點點!當然,實際專案中,很多優秀程式設計師都是推薦代理和block混合使用,這樣能夠提高程式碼效率。自己慢慢體會吧。

好了,今天就寫到這吧,這是我的第一篇部落格,寫的不太清楚,毛病也很多,可能還有一些地方不夠準確,所以歡迎指正,希望可以分享學習更多更好的東西,謝謝大家。

相關文章