深度解析Cocoa異步請求和libxml2.dylib教程是本文要介紹的內容,不多說,直接進入話題,很早就在cocoachina上看到這個框架了,今天終于有機會來使用這個東東了.
我這里寫一下,如何往iphone項目中添加這個框架.
步驟如下:
1.下載該framework : http://github.com/pokeb/asi-http-request/tree
2.將class根目錄下的文件全拷貝到自己的項目中,另外還要在 External/Reachability/下將其中的Reachability.h/m
也拷貝到自己的項目中.
3.添加需要的framework.可以參考 http://allseeing-i.com/ASIHTTPRequest/Setup-instructions
需要額外添加的有: CFNetwork.framework, MobileCoreServices.framework,SystemConfiguration.framework,libz.1.2.3.dylib,libxml2.dylib
然后運行項目,會發現有很多xml相關的error,不用急,這時因為libxml2.dylib這個framework(這個框架不是很friendly,我們還需要做一些工作).
在xcode中project->edit project settings->然后search "search paths",然后在path中添加 /usr/include/libxml2
這樣就ok了,可以根據官方的教程來學習了.
http://allseeing-i.com/ASIHTTPRequest/How-to-use
我下了一個sample code? XMLPerformance 解析xml,我建了一個工程照著上面做,但是編譯時提示錯誤,
error?libxml/tree.h:?No?such?file?or?directory?我立刻想到沒有add Frameworks ,我把libsqlite3.dylib 和 libxml2.dylib都加進去了,但是還是報錯。
error?libxml/tree.h:?No?such?file?or?directory ?An?error?on?the?.h?is?a?compile-time?error?with?your?Header?Search?Paths,?not?a?.dylib?or?a?linker?error. ?You?have?to?ensure?that?/usr/include/libxml2?is?in?your?Header?Search?Paths?in?your?Release?configuration。?在iphone開發中,異步操作是一個永恒的話題,尤其當iphone手機需要和遠程服務器進行交互時,使用異步請求是很普遍的做法。
通常,這需要NSURLConnection和NSOperation結合起來使用。這方面的資料網絡上自然有不少的介紹,不過要找一個能運行的代碼也并不容易。許多文章介紹的并不全面,或者使用了過時的SDK,在新IOS版本下并不適用(當前最新的ios是4.2了)。這些代碼很經典,但仍然很容易使人誤入歧途。
本文總結了眾多文檔介紹的方法和代碼,揭示了異步操作中的實現細節和初學者(包括筆者)易犯的錯誤,使后來者少走彎路。
一、使用NSOperation實現異步請求
1、新建類,繼承自NSOperation。
@interface?URLOperation?:?NSOperation ?{ ?????NSURLRequest*??_request; ?????NSURLConnection*?_connection; ?????NSMutableData*?_data; ?????//構建gb2312的encoding ?????NSStringEncoding?enc; ?} ?-?(id)initWithURLString:(NSString?*)url; ?@property?(readonly)?NSData?*data; ?@end?接口部分不多做介紹,我們來看實現部分。
首先是帶一個NSString參數的構造函數。在其中初始化成員變量。
其中enc是 NSStringEncoding 類型,因為服務器返回的字符中使用了中文,所以我們通過它指定了一個gb2312的字符編碼。
許多資料中說,需要在NSOperation中重載一個叫做isConcurrent的函數并在其中返回YES,否則不支持異步執行。但是實際上,我們在這里注釋了這個重載方法,程序也沒有報任何錯誤,其執行方式依然是異步的。
@implementation?URLOperation ?@synthesize?data=_data; ?-?(id)initWithURLString:(NSString?*)url?{ ?????if?(self?=?[self?init])?{ ?????????NSLog(@"%@",url); ?????????_request?=?[[NSURLRequest?alloc]?initWithURL:[NSURL?URLWithString:url ?????????//構建gb2312的encoding ?????????enc?=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); ?????????_data?=?[[NSMutableData?data]?retain]; ?????} ?????return?self; ?} ?-?(void)dealloc?{ ?????[_request?release],_request=nil; ?????[_data?release],_data=nil; ?????[_connection?release],_connection=nil; ?????[super?dealloc]; ?} ?//?如果不重載下面的函數,異步方式調用會出錯 ?//-?(BOOL)isConcurrent?{ ?//如果采用并發,就不要使用queue,add5次后會發生運行錯誤//??return?YES;//返回yes表示支持異步調用,否則為支持同步調用 ?//}?整個類中最重要的方法是start方法。Start是NSOperation類的主方法,主方法的叫法充分說明了其重要性,因為這個方法執行完后,該NSOperation的執行線程就結束了(返回調用者的主線程),同時對象實例就會被釋放,也就意味著你定義的其他代碼(包括delegate方法)也不會被執行。很多資料中的start方法都只有最簡單的一句(包括“易飛揚的博客 “的博文):
[NSURLConnection?connectionWithRequest:_request?delegate:self];?如果這樣的話,delegate方法沒有執行機會。因為start方法結束后delegate(即self對象)已經被釋放了,delegate的方法也就無從執行。
所以在上面的代碼中,還有一個while循環,這個while循環的退出條件是http連接終止(即請求結束)。當循環結束,我們的工作也就完成了。
//?開始處理-本類的主方法 ?-?(void)start?{ ?????if?(![self?isCancelled])?{ ?????????NSLog(@"start?operation"); ?????????//?以異步方式處理事件,并設置代理 ?????????_connection=[[NSURLConnection?connectionWithRequest:_request?delegate:self]retain]; ?????????//下面建立一個循環直到連接終止,使線程不離開主方法,否則connection的delegate方法不會被調用,因為主方法結束對象的生命周期即終止 ?????????//這個問題參考?http://www.cocoabuilder.com/archive/cocoa/279826-nsurlrequest-and-nsoperationqueue.html ?????????while(_connection?!=?nil)?{ ?????????????[[NSRunLoop?currentRunLoop]?runMode:NSDefaultRunLoopMode?beforeDate:[NSDate?distantFuture]];??? ?????????} ?????} ?}?接下來,是NSURLConnection的delegate方法,這部分的代碼和大部分資料的介紹是一樣的,你可以實現全部的delegate方法,但這里我們只實現其中3個就足夠了,其余的方法不用理會。如你所見,你可以在其中添加自己想到的任何代碼,包括接收數據,進行字符編碼或者做xml解析。
#pragma?mark?NSURLConnection?delegate?Method ?//?接收到數據(增量)時 ?-?(void)connection:(NSURLConnection*)connection ?????didReceiveData:(NSData*)data?{ ?????NSLog(@"connection:"); ?????NSLog(@"%@",[[NSString?alloc]?initWithData:data?encoding:enc]); ?????//?添加數據 ??????[_data?appendData:data]; ??} ?//?HTTP請求結束時 ?-?(void)connectionDidFinishLoading:(NSURLConnection*)connection?{ ?? ? [_connection?release],_connection=nil; ?????//NSLog(@"%@",[[NSString?alloc]?initWithData:_data?encoding:enc]); ?} ?-(void)connection:?(NSURLConnection?*)?connection?didFailWithError:?(NSError?*)?error{ ?????NSLog(@"connection?error"); ?} ?@end?到此,雖然代碼還沒有完成,但我們已經可以運行它了。你可以看到console輸出的內容,觀察程序的運行狀態。
2、調用NSOperation
我們的NSOperation類可以在ViewController中調用,也可以直接放在AppDelegate中進行。
在這里,我是通過點擊按鈕來觸發調用代碼的:
-(void)loginClicked{ ?????//構造登錄請求url ?????NSString*?url=@”http://google.com”; ?????_queue?=?[[NSOperationQueue?alloc]?init]; ?????URLOperation*?operation=[[URLOperation?alloc?]initWithURLString:url]; ?????//?開始處理 ?????[_queue?addOperation:operation]; ?????[operation?release];//隊列已對其retain,可以進行release; ?}?_queue是一個 NSOperationQueue 對象,當往其中添加 NSOperation 對象后, NSOperation 線程會被自動執行(不是立即執行,根據調度情況)。
3、KVO編程模型
我們的NSOperation完成了向服務器的請求并將服務器數據下載到成員變量_data中了?,F在的問題是,由于這一切是通過異步操作進行的,我們無法取得_data中的數據,因為我們不知道什么時候異步操作完成,以便去訪問_data屬性(假設我們將_data定義為屬性了),取得服務器數據。
我們需要一種機制,當NSOperation完成所有工作之后,通知調用線程。
這里我們想到了KVO編程模型(鍵-值觀察模型)。這是cocoa綁定技術中使用的一種設計模式,它可以使一個對象在屬性值發生變化時主動通知另一個對象并觸發相應的方法。
首先,我們在NSOperation的子類中添加一個BOOL變量,當這個變量變為YES時,標志異步操作已經完成:
BOOL?_isFinished;?在實現中加入這個變量的訪問方法:
-?(BOOL)isFinished ?{ ?????return?_isFinished; ?}?cocoa的KVO模型中,有兩種通知觀察者的方式,自動通知和手動通知。顧名思義,自動通知由cocoa在屬性值變化時自動通知觀察者,而手動通知需要在值變化時調用 willChangeValueForKey:和didChangeValueForKey: 方法通知調用者。為求簡便,我們一般使用自動通知。
要使用自動通知,需要在 automaticallyNotifiesObserversForKey方法中明確告訴cocoa,哪些鍵值要使用自動通知:
//重新實現NSObject類中的automaticallyNotifiesObserversForKey:方法,返回yes表示自動通知。 ?+?(BOOL)automaticallyNotifiesObserversForKey:(NSString*)key ?{ ?????//當這兩個值改變時,使用自動通知已注冊過的觀察者,觀察者需要實現observeValueForKeyPath:ofObject:change:context:方法 ?????if?([key?isEqualToString:@"isFinished"]) ?????{ ?????????return?YES; ?????} ?????return?[super?automaticallyNotifiesObserversForKey:key]; ?}?然后,在需要改變_isFinished變量的地方,使用
[self?setValue:[NSNumber?numberWithBool:YES]?forKey:@"isFinished"];?方法,而不是僅僅使用簡單賦值。
我們需要在3個地方改變isFinished值為YES,請求結束時、連接出錯誤,線程被cancel。請在對應的方法代碼中加入上面的語句。
最后,需要在觀察者的代碼中進行注冊。打開ViewController中調用NSOperation子類的地方,加入:
????//kvo注冊 ?????[operation?addObserver:self?forKeyPath:@"isFinished" ????????????????????options:(NSKeyValueObservingOptionNew?|?NSKeyValueObservingOptionOld)?context:operation]; ?并實現?observeValueForKeyPath?方法: ?//接收變更通知 ?-?(void)observeValueForKeyPath:(NSString?*)keyPath ???????????????????????ofObject:(id)object ????????????????????????change:(NSDictionary?*)change ????????????????????????context:(void?*)context ?{ ?????if?([keyPath?isEqual:@"isFinished"])?{ ?????????BOOL?isFinished=[[change?objectForKey:NSKeyValueChangeNewKey]?intValue]; ?????????if?(isFinished)?{//如果服務器數據接收完畢 ?????????????[indicatorView?stopAnimating]; ?????????????URLOperation*?ctx=(URLOperation*)context; ?????????????NSStringEncoding?enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); ?????????????NSLog(@"%@",[[NSString?alloc]?initWithData:[ctx?data]?encoding:enc]); ?????????????//取消kvo注冊 ?????????????[ctx?removeObserver:self ?????????????????????forKeyPath:@"isFinished"]; ?????????}?????? ?????}else{ ?????????//?be?sure?to?call?the?super?implementation ?????????//?if?the?superclass?implements?it ?????????[super?observeValueForKeyPath:keyPath ??????????????????????????????ofObject:object ????????????????????????????????change:change ???????????????????????????????context:context]; ?????} ?}?運行程序,查看控制臺的輸出。
?
轉載于:https://www.cnblogs.com/pengyingh/articles/2355273.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的深度解析Cocoa异步请求和libxml2.dylib教程(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。