標籤:des style blog http io ar color os 使用
不知不覺畢業後也工作了快半年了,時光荏苒總覺得若再不寫點什麼,留下點痕迹,我都快把自己遺忘了。從接觸iOS到現在初略算有一年多了吧,我很想把自己所看所想所領悟的一些技巧給大家分享,但自身開發經驗也不算成熟,今天厚一厚臉皮,寫自己的第一篇部落格,我想只要不誤人子弟,這次就算我贏了吧。下面進入正題——構建iOS的網路業務請求架構。
網路服務幾乎是每一款成功APP的必備條件,開啟你手機你會發現裡面不用連網的應用數量十隻手指可以數出來,就算是一些以獨特技術切入市場的APP如美顏相機,都至少加入了分享功能。下面我先做下簡單的回顧兼掃盲。
蘋果定義了NSConnection 這個類來專門處理網路請求,並且這個類有兩種用法來滿足開發人員需求。
用法一,同步請求
//NSURLConnection.h
@interface NSURLConnection (NSURLConnectionSynchronousLoading)
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;
@end
//**.mNSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:error];
這種用法比較少人使用,因為他無法捕捉傳輸過程的內容,並且需要在子線程中使用,不然將會卡住主線程的UI繪製,直到sendSynchronousRequest返回data,但勝在簡單易用,一目瞭然。
用法二,非同步請求
//NSURLConnection.h
@interface NSURLConnection : NSObject
- (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately; - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; - (void)start;
@end
//**.m
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
相比第一種用法,第二種頓時讓人云裡霧裡,特別nsrunloop更讓人疑問,另外,上面的代碼僅僅是開始請求部分,資料接收部分的代碼還沒貼出來。
細心的人注意到,在.m檔案中,NSConnection初始化時傳入了一個delegate,是的,資料便是通過NSURLConnectionDataDelegate的回調來處理的。
//NSURLConnection.h
@protocol NSURLConnectionDataDelegate <NSURLConnectionDelegate>@optional
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;@end
為了不讓其他函數擾亂了大家的實現,這裡我只貼出跟本文有關的函數,從字面上大家都能知道這他們的作用了,didReceiveData會攜帶著請求下來的資料data多次回調,didFinishLoading是在請求結束後調用,另外還有一個didFailWithError在請求失敗是調用,只不錯他是老爸NSURLConnectionDelegate的內容。
下面我們回到第二種用法的.m檔案,這裡面涉及到runloop的執行機制,我在這裡只解釋為什麼要這樣設定。
Runloop有五種運行模式,這裡我只說三種:
1,NSDefaultRunLoopMode: 預設的運行模式,除了NSConnection對象的事件;
2,UITrackingRunLoopMode: 用於跟蹤觸摸事件觸發的模式(例如UIScrollView上下滾動),主線程當觸摸事件觸發時會設定為這個模式;
3,NSRunLoopCommonModes: 是一組常用的模式集合,簡單來將就是模式集合,既然是集合,當然包括上面的兩種模式。
預設情況下NSURLConnection,NSTimer都是運行1模式下,那麼問題來了,假如這個時候使用者刷一下螢幕,此時切換為模式2,而這時候剛好有資料請求下來,那麼就悲劇了,didReceiveData不會回調。(如果NSTimer涉及精密的資料計算,這裡也要注意下)解決辦法就是替換成NSRunLoopCommonModes。
著名的SDWebImage在網路非同步請求圖片時也是使用第二種方法,個人覺得這種方法相當高效,在不建立新線程的情況下,依靠runloop來監聽網路請求資料,如果同學們還是有點淩亂,不妨試下將NSURLConnection替換成NSTimer這樣,其實原理都差不多。
另外這裡有一點NSConnection在初始化的時候要注意startImmediately:不能設定為YES,如果你設定為YES,後在設定runloopmode是無效的。
現在理解了網路請求最具高效的方案(個人認為。。。),有沒有感覺這些代理什麼挺繁瑣的,而一個APP不可能只有一個網路請求介面,這時候我們就需要一個管理者的角色,我們暫時將它命名為HttpManager。一個manager統領著一群connection,這時候你發現問題又來了,connection從外表上看長得都一樣,沒有可以標記的東西,於是,我們給他造個子類MyURLConnection 並且加多一個tag欄位,來互相辨認。:
有了tag後,tag用什麼來裝填好呢,對tag唯一的要求就是不重複,這裡我就直接使用url來做tag,因為除非奇葩需求,否側url重複請求確實沒有必要。
在didReceivaData中,_dataTagMap跟據tag,追加資料;
在sendHttpRequestWithUrl中,先檢查_connectionTagMap中是否已經有以該url為key的對象了,有就說明已經這條請求,這條請求可以丟棄了。沒有就new一個MyURLConnection 並將url作為tag,最後以url為key ,myconnection為value填入_connectionTagMap中;
在didFinishLoading中,根據connect的tag值,將對應的connection從_dataTagMap中提出來並發給對應的Model層處理,並從_dataTagMap和_connectionTagMap移除相應項;
好像聽起來很簡單的樣子,實際上,也很簡單。。。這裡的發給Model層可能有些同學有迷糊了,什麼叫發?大家注意到前面_connectionTagMap如果發現有重複直接丟棄即return,所以這要求HttpManager和與之互動的類必須低耦合,所以這裡的發意思是指使用通知
[[NSNotificationCenter defaultCenter] postNotificationName:KHTTPMgrNotificationSendRequestCompleted object:self userInfo:info];
info其實就一個NSDictionary,裡面你想裝什麼就裝什麼,用過都知道。
今天先到這裡吧,本來想寫兩種請求架構對比,結果唯寫了一種也唯寫了一半,只好下周末再補上對應的網路業務Model層以及另外一種請求架構了。
iOS WebServiceFramework網路服務架構淺解