精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

iPhone開發中使用NSOperation實現異步下載

移動開發 iOS
iPhone開發中使用NSOperation實現異步下載是本文要介紹的內容,在iphone開發中,異步操作是一個永恒的話題,尤其當iphone手機需要和遠程服務器進行交互時,使用異步請求是很普遍的做法。

iPhone開發中使用NSOperation實現異步下載是本文要介紹的內容,在iphone開發中,異步操作是一個永恒的話題,尤其當iphone手機需要和遠程服務器進行交互時,使用異步請求是很普遍的做法。

通常,這需要NSURLConnection和 NSOperation結合起來使用。這方面的資料網絡上自然有不少的介紹,不過要找一個能運行的代碼也并不容易。許多文章介紹的并不全面,或者使用了過 時的SDK,在新IOS版本下并不適用(當前***的ios是4.2了)。這些代碼很經典,但仍然很容易使人誤入歧途。

本文總結了眾多文檔介紹的方法和代碼,揭示了異步操作中的實現細節和初學者(包括筆者)易犯的錯誤,使后來者少走彎路。

一、使用NSOperation實現異步請求

1、新建類,繼承自NSOperation。

  1. @interfaceURLOperation : NSOperation  
  2. {  
  3.     NSURLRequest*  _request;  
  4.     NSURLConnection* _connection;  
  5.     NSMutableData* _data;  
  6.     //構建gb2312的encoding  
  7.     NSStringEncodingenc;  
  8. }  
  9. - (id)initWithURLString:(NSString*)url;  
  10. @property(readonly) NSData *data;  
  11. @end 

接口部分不多做介紹,我們來看實現部分。

首先是帶一個NSString參數的構造函數。在其中初始化成員變量。

其中enc是NSStringEncoding類型,因為服務器返回的字符中使用了中文 ,所以我們通過它指定了一個gb2312的字符編碼。

許多資料中說,需要在NSOperation中重載一個叫做isConcurrent的函數并在其中返回YES,否則不支持異步執行。但是實際上,我們在這里注釋了這個重載方法,程序也沒有報任何錯誤,其執行方式依然是異步的。

  1. @implementationURLOperation  
  2. @synthesizedata=_data;  
  3. - (id)initWithURLString:(NSString*)url {  
  4.     if(self= [selfinit]) {  
  5.         NSLog(@"%@",url);  
  6.         _request= [[NSURLRequestalloc] initWithURL:[NSURLURLWithString:url  
  7.         //構建gb2312的encoding  
  8.         enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);  
  9.         _data= [[NSMutableDatadata] retain];  
  10.     }  
  11.     returnself;  
  12. }  
  13. - (void)dealloc {  
  14.     [_requestrelease],_request=nil;  
  15.     [_datarelease],_data=nil;  
  16.     [_connectionrelease],_connection=nil;  
  17.     [superdealloc];  
  18. }  
  19. // 如果不重載下面的函數,異步方式調用會出錯  
  20. //- (BOOL)isConcurrent {  
  21. //  return YES;//返回yes表示支持異步調用,否則為支持同步調用  
  22. //} 

整個類中最重要的方法是start方法。Start是 NSOperation類的主方法,主方法的叫法充分說明了其重要性,因為這個方法執行完后,該NSOperation的執行線程就結束了(返回調用者的 主線程),同時對象實例就會被釋放,也就意味著你定義的其他代碼(包括delegate方法)也不會被執行。很多資料中的start方法都只有最簡單的一 句(包括“易飛揚的博客 “的博文):

  1. [NSURLConnection connectionWithRequest:_request delegate:self]; 

如果這樣的話,delegate方法沒有執行機會。因為start方法結束后delegate(即self對象)已經被釋放了,delegate的方法也就無從執行。

所以在上面的代碼中,還有一個while循環,這個while循環的退出條件是http連接終止(即請求結束)。 當循環結束,我們的工作也就完成了。

  1. // 開始處理-本類的主方法  
  2. - (void)start {  
  3.     if(![selfisCancelled]) {  
  4.         NSLog(@"start operation");  
  5.         // 以異步方式處理事件,并設置代理  
  6.         _connection=[[NSURLConnectionconnectionWithRequest:_requestdelegate:self]retain];  
  7.         //下面建立一個循環直到連接終止,使線程不離開主方法,否則connection的delegate方法不會被調用,因為主方法結束對象的生命周期即終止  
  8.         //這個問題參考http://www.cocoabuilder.com/archive/cocoa/279826-nsurlrequest-and-nsoperationqueue.html  
  9.         while(_connection!= nil) {  
  10.             [[NSRunLoopcurrentRunLoop] runMode:NSDefaultRunLoopModebeforeDate:[NSDatedistantFuture]];     
  11.         }  
  12.     }  

接下來,是NSURLConnection的 delegate方法,這部分的代碼和大部分資料的介紹是一樣的,你可以實現全部的delegate方法,但這里我們只實現其中3個就足夠了,其余的方法 不用理會。如你所見,你可以在其中添加自己想到的任何代碼,包括接收數據,進行字符編碼或者做xml解析。

  1. #pragma mark NSURLConnection delegate Method  
  2. // 接收到數據(增量)時  
  3.  
  4. - (void)connection:(NSURLConnection*)connection  
  5.     didReceiveData:(NSData*)data {  
  6.     NSLog(@"connection:");  
  7.     NSLog(@"%@",[[NSStringalloc] initWithData:data encoding:enc]);  
  8.     // 添加數據  
  9.     [_dataappendData:data];  
  10. }  
  11. // HTTP請求結束時  
  12. - (void)connectionDidFinishLoading:(NSURLConnection*)connection {  
  13.     [_connectionrelease],_connection=nil;  
  14.     //NSLog(@"%@",[[NSString alloc] initWithData:_data encoding:enc]);  
  15. }  
  16. -(void)connection: (NSURLConnection*) connection didFailWithError: (NSError*) error{  
  17.     NSLog(@"connection error");  
  18. }  
  19.  
  20. @end 

到此,雖然代碼還沒有完成,但我們已經可以運行它了。你可以看到console輸出的內容,觀察程序的運行狀態。

2、調用NSOperation

我們的NSOperation類可以在ViewController中調用,也可以直接放在AppDelegate中進行。

在這里,我是通過點擊按鈕來觸發調用代碼的:

  1. -(void)loginClicked{  
  2.     //構造登錄請求url  
  3.     NSString* url=@”http://google.com”;  
  4.     _queue= [[NSOperationQueuealloc] init];  
  5.     URLOperation* operation=[[URLOperationalloc ]initWithURLString:url];  
  6.     // 開始處理  
  7.     [_queueaddOperation:operation];  
  8.     [operation release];//隊列已對其retain,可以進行release;  

_queue是一個NSOperationQueue對象,當往其中添加 NSOperation 對象后, NSOperation 線程會被自動執行(不是立即執行,根據調度情況)。

3、KVO編程模型

我們的NSOperation完成了向服務器的請求并將 服務器數據下載到成員變量_data中了。現在的問題是,由于這一切是通過異步操作進行的,我們無法取得_data中的數據,因為我們不知道什么時候異步 操作完成,以便去訪問_data屬性(假設我們將_data定義為屬性了),取得服務器數據。

我們需要一種機制,當NSOperation完成所有工作之后,通知調用線程。

這里我們想到了KVO編程模型(鍵-值觀察模型)。這是cocoa綁定技術中使用的一種設計模式,它可以使一個對象在屬性值發生變化時主動通知另一個對象并觸發相應的方法。具體請參考cocoa參考庫:

  1. http://www.apple.com.cn/developer/mac/library/documentation/Cocoa/Conceptual/CocoaBindings/index.html,  
  2. 以及   
  3. http://www.apple.com.cn/developer/mac/library/documentation/Cocoa/Conceptual/KeyValueObserving/Concepts/
  4. KVOBasics.html#//apple_ref/doc/uid/20002252  

兩篇文檔。

首先,我們在NSOperation的子類中添加一個BOOL變量,當這個變量變為YES時,標志異步操作已經完成:

  1. BOOL_isFinished; 

在實現中加入這個變量的訪問方法:

  1. - (BOOL)isFinished  
  2. {  
  3.     return_isFinished;  

cocoa的KVO模型中,有兩種通知觀察者的方式,自動通知和手動通知。顧名思義,自動通知由cocoa在屬性值變化時自動通知觀察者,而手動通知需要在值變化時調用willChangeValueForKey:和didChangeValueForKey:方法通知調用者。 為求簡便,我們一般使用自動通知。

要使用自動通知,需要在automaticallyNotifiesObserversForKey方法中明確告訴cocoa,哪些鍵值要使用自動通知:

  1. //重新實現NSObject類中的automaticallyNotifiesObserversForKey:方法,返回yes表示自動通知。  
  2.  
  3. + (BOOL):(NSString*)key  
  4. {  
  5.     //當這兩個值改變時,使用自動通知已注冊過的觀察者,觀察者需要實現observeValueForKeyPath:ofObject:change:context:方法  
  6.     if([key isEqualToString:@"isFinished"])  
  7.     {  
  8.         returnYES;  
  9.     }  
  10.     return[superautomaticallyNotifiesObserversForKey:key];  

然后,在需要改變_isFinished變量的地方,使用

  1. [selfsetValue:[NSNumbernumberWithBool:YES] forKey:@"isFinished"]; 

方法,而不是僅僅使用簡單賦值。

我們需要在3個地方改變isFinished值為YES, 請求結束時、連接出錯誤,線程被cancel。請在對應的方法代碼中加入上面的語句。

***,需要在觀察者的代碼中進行注冊。打開ViewController中調用NSOperation子類的地方,加入:

  1.     //kvo注冊  
  2.     [operation addObserver:selfforKeyPath:@"isFinished"  
  3.                    options:(NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld) context:operation];  
  4.                      
  5. 并實現observeValueForKeyPath方法:  
  6.  
  7. //接收變更通知  
  8. - (void)observeValueForKeyPath:(NSString*)keyPath  
  9.                       ofObject:(id)object  
  10.                         change:(NSDictionary*)change  
  11.                        context:(void*)context  
  12. {  
  13.     if([keyPath isEqual:@"isFinished"]) {  
  14.         BOOLisFinished=[[change objectForKey:NSKeyValueChangeNewKey] intValue];  
  15.         if(isFinished) {//如果服務器數據接收完畢  
  16.             [indicatorViewstopAnimating];  
  17.             URLOperation* ctx=(URLOperation*)context;  
  18.             NSStringEncodingenc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);  
  19.             NSLog(@"%@",[[NSStringalloc] initWithData:[ctx data] encoding:enc]);  
  20.             //取消kvo注冊  
  21.             [ctx removeObserver:self  
  22.                      forKeyPath:@"isFinished"];  
  23.         }        
  24.     }else{  
  25.         // be sure to call the super implementation  
  26.         // if the superclass implements it  
  27.         [superobserveValueForKeyPath:keyPath  
  28.                              ofObject:object  
  29.                                change:change  
  30.                               context:context];  
  31.     }  

運行程序,查看控制臺的輸出。

4、libxml的sax解析接口

iphone和服務器交互通常使用xml數據交換格式,因此本文中也涉及到了xml文件解析的問題。有許多有名氣的xml解析器可供我們選擇, 如: BXML,TouchXML,KissXML,TinyXML的第三方庫和GDataXML。

Xml解析分為兩類,一類是DOM解析,一類為SAX解析。前者如GDataXML,解析過程中需要建立文檔樹,操作XML元素時通過樹形結構進行導航。DOM解析的特點是便于程序員理解xml文檔樹結構,API 的使用簡單;缺點是速度較SAX解析慢,且內存開銷較大。在某些情況下, 比如iphone開發,受制于有限的內存空間(一個應用最多可用10幾m的內存), DOM解析無法使用(當然,在模擬器上是沒有問題的)。

libxml2的是一個開放源碼庫,默認情況下iPhone SDK 中已經包括在內。 它是一個基于C的API,所以在使用上比cocoa的 NSXML要麻煩許多(一種類似c函數的使用方式),但是該庫同時支持DOM和SAX解析,其解析速度較快,而且占用內存小,是最適合使用在iphone上的解析器。 從性能上講,所有知名的解析器中,TBXML最快,但在內存占用上,libxml使用的內存開銷是最小的。因此,我們決定使用libxml的sax接口。

首先,我們需要在project中導入framework:libxml2.dylib。

雖然libxml是sdk中自帶的,但它的頭文件卻未放在默認的地方,因此還需要我們設置project的build選項:HEADER_SEARCH_PATHS = /usr/include/libxml2,否則libxml庫不可用。

然后,我們就可以在源代碼中 #import <libxml/tree.h>了。

假設我們要實現這樣的功能:有一個登錄按鈕,點擊后將用戶密碼帳號發送http請求到服務器(用上文中介紹的異步請求技術),服務器進行驗證后以xml文件方式返回驗證結果。我們要用libxml的sax方式將這個xml文件解析出來。

服務器返回的xml文件格式可能如下:

  1. <?xml version="1.0" encoding="GB2312" standalone="no" ?> 
  2. <root> 
  3. <login_info> 
  4. <login_status>true</login_status> 
  5. </login_info> 
  6. <List> 
  7.         <system Name=xxx Path=xxx ImageIndex=xxx> 
  8. ……  
  9. </List> 
  10. </root> 

其中有我們最關心的1個元素:login_status 。

如果login_status返回false,說明登錄驗證失敗,否則,服務器除返回login_status外,還會返回一個list元素,包含了一些用戶的數據,這些數據是<system>元素的集合。

整個實現步驟見下。

首先,實現一個超類, 這個超類是一個抽象類,許多方法都只是空的,等待subclass去實現。

其中有3個方法與libxml的sax接口相關,是sax解析過程中的3個重要事件的回調方法,分別是元素的開始標記、元素體(開始標記和結束標記之間的文本)、結束標記。Sax中有許多的事件,但絕大部分時間,我們只需要處理這3個事件。 因為很多時候,我們只會對xml文件中的元素屬性和內容感興趣,而通過這3個事件已經足以使我們讀取到xml節點的屬性和內容 。

而成員變量中,_root變量是比較關鍵的,它以dictionary的形式保存了解析結果,因為任何xml文檔的根節點都是root,所以無論什么樣子的xml文件,都可以放在這個_root 中。

因此我們為 _root 變量提供了一個訪問方法getResult,等xml解析結束,可以通過這個方法訪問_root。

  1. #import <Foundation/Foundation.h> 
  2. #import <libxml/tree.h> 
  3. @interfaceBaseXmlParser : NSObject {  
  4.     NSStringEncodingenc;  
  5.     NSMutableDictionary*    _root;  
  6. }  
  7. // Property  
  8. - (void)startElementLocalName:(constxmlChar*)localname  
  9.                        prefix:(constxmlChar*)prefix  
  10.                           URI:(constxmlChar*)URI  
  11.                 nb_namespaces:(int)nb_namespaces  
  12.                    namespaces:(constxmlChar**)namespaces  
  13.                 nb_attributes:(int)nb_attributes  
  14.                  nb_defaulted:(int)nb_defaultedslo  
  15.                    attributes:(constxmlChar**)attributes;  
  16. - (void)endElementLocalName:(constxmlChar*)localname  
  17.                      prefix:(constxmlChar*)prefix URI:(constxmlChar*)URI;  
  18. - (void)charactersFound:(constxmlChar*)ch  
  19.                     len:(int)len;  
  20. -(NSDictionary*)getResult;  
  21. @end  
  22. #import "BaseXmlParser.h"  
  23. @implementationBaseXmlParser  
  24. // Property  
  25. -(id)init{  
  26.     if(self=[superinit]){  
  27.         //構建gb2312的encoding  
  28.         enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);  
  29.         _root=[[NSMutableDictionaryalloc]init];  
  30.     }  
  31.     returnself;  
  32. }  
  33. -(void)dealloc{  
  34.     [_rootrelease],_root=nil;  
  35.     [superdealloc];  
  36. }  
  37. //--------------------------------------------------------------//  
  38. #pragma mark -- libxml handler,主要是3個回調方法--  
  39. //--------------------------------------------------------------//  
  40. //解析元素開始標記時觸發,在這里取元素的屬性值  
  41. - (void)startElementLocalName:(constxmlChar*)localname  
  42.                        prefix:(constxmlChar*)prefix  
  43.                           URI:(constxmlChar*)URI  
  44.                 nb_namespaces:(int)nb_namespaces  
  45.                    namespaces:(constxmlChar**)namespaces  
  46.                 nb_attributes:(int)nb_attributes  
  47.                  nb_defaulted:(int)nb_defaultedslo  
  48.                    attributes:(constxmlChar**)attributes  
  49. {    
  50. }  
  51. //解析元素結束標記時觸發  
  52. - (void)endElementLocalName:(constxmlChar*)localname  
  53.                      prefix:(constxmlChar*)prefix URI:(constxmlChar*)URI  
  54. {  
  55. }  
  56. //解析元素體時觸發  
  57. - (void)charactersFound:(constxmlChar*)ch  
  58.                     len:(int)len  
  59. {  
  60. }  
  61. //返回解析結果  
  62. -(NSDictionary*)getResult{  
  63.     return_root;  
  64. }  
  65. @end 

現在我們需要擴展這個BaseXmlParser,并重載其中的3個sax方法。

該子類除了重載父類的3個方法外,還增加了幾個成員變 量。其中flag是一個int類型,用于sax解析的緣故,解析過程中需要合適的標志變量,用于標志當前處理到的元素標記。為了簡單起見,我們沒有為每一 個標記都設立一個標志,而是統一使用一個int標志,比如flag為1時,表示正在處理login_status標記,為2時,表示正在處理system 標記。

回顧前面的xml文件格式,我們其實只關心兩種標記,login_status標記和system標記。Login_status標記沒有屬性,但它的元素體是我們關心的;而system標記則相反,它并沒有元素體,但我們需要它的屬性值。

這是一個很好的例子。因為它同時展示了屬性的解析和元素體的解析。瀏覽整個類的代碼,我們總結出3個sax事件的使用規律是:

如果要讀取元素屬性,需要在“元素開始標記讀取”事件(即startElementLocalName方法)中處理;

如果要讀取元素體文本,則在“元素體讀取”事件(即charactersFound方法)中處理;

在“元素標記讀取”事件(即endElementLocalName方法)中,則進行標志變量的改變/歸零。

  1. #import <Foundation/Foundation.h> 
  2. #import <libxml/tree.h> 
  3. #import "BaseXmlParser.h"  
  4. @interfaceDLTLoginParser : BaseXmlParser {  
  5.     intflag;  
  6.     NSMutableDictionary*    _currentItem;    
  7. }  
  8. - (void)startElementLocalName:(constxmlChar*)localname  
  9.                        prefix:(constxmlChar*)prefix  
  10.                           URI:(constxmlChar*)URI  
  11.                 nb_namespaces:(int)nb_namespaces  
  12.                    namespaces:(constxmlChar**)namespaces  
  13.                 nb_attributes:(int)nb_attributes  
  14.                 nb_defaulted:(int)nb_defaultedslo  
  15.                   attributes:(constxmlChar**)attributes;  
  16. - (void):(constxmlChar*)localname  
  17.                      prefix:(constxmlChar*)prefix URI:(constxmlChar*)URI;  
  18. - (void)charactersFound:(constxmlChar*)ch  
  19.                     len:(int)len;  
  20. @end  
  21.  
  22. #import "DLTLoginParser.h"  
  23. @implementationDLTLoginParser  
  24. -(id)init{  
  25.     if(self=[superinit]){  
  26.         NSMutableArray* items=[[NSMutableArrayalloc]init];  
  27.         [_rootsetObject:items forKey:@"items"];  
  28.         [items release];//已被_root持有了,可以釋放  
  29.     }  
  30.     returnself;  
  31. }  
  32. -(void)dealloc{  
  33.     [_currentItemrelease],_currentItem=nil;  
  34.     [superdealloc];  
  35. }  
  36. //--------------------------------------------------------------//  
  37. #pragma mark -- libxml handler,主要是3個回調方法--  
  38. //--------------------------------------------------------------//  
  39. //解析元素開始標記時觸發,在這里取元素的屬性值  
  40. - (void)startElementLocalName:(constxmlChar*)localname  
  41.                        prefix:(constxmlChar*)prefix  
  42.                           URI:(constxmlChar*)URI  
  43.                 nb_namespaces:(int)nb_namespaces  
  44.                    namespaces:(constxmlChar**)namespaces  
  45.                 nb_attributes:(int)nb_attributes  
  46.                  nb_defaulted:(int)nb_defaultedslo  
  47.                    attributes:(constxmlChar**)attributes  
  48. {  
  49.     // login_status,置標志為1  
  50.     if(strncmp((char*)localname, "login_status", sizeof("login_status")) == 0) {  
  51.         flag=1;  
  52.         return;  
  53.     }  
  54.     // system,置標志為2  
  55.     if(strncmp((char*)localname, "system", sizeof("system")) == 0) {  
  56.         flag=2;  
  57.         _currentItem= [NSMutableDictionarydictionary];  
  58.         //查找屬性  
  59.         NSString*key,*val;  
  60.         for(inti=0; i<nb_attributes; i++){  
  61.             key = [NSStringstringWithCString:(constchar*)attributes[0] encoding:NSUTF8StringEncoding];  
  62.             val = [[NSStringalloc] initWithBytes:(constvoid*)attributes[3] length:(attributes[4] - attributes[3]) encoding:NSUTF8StringEncoding];  
  63.             NSLog(@"key=%@,val=%@",key,val);  
  64.             if([@"Name"isEqualToString:key]) {  
  65.                 [_currentItemsetObject:val forKey:@"name"];  
  66.                 break;  
  67.             }  
  68.             // [val release];  
  69.             attributes += 5;//指針移動5個字符串,到下一個屬性  
  70.         }  
  71.         [[_rootobjectForKey:@"items"] addObject:_currentItem];  
  72.         return;  
  73.     }  

//解析元素結束標記時觸發

  1. - (void)endElementLocalName:(constxmlChar*)localname  
  2.                      prefix:(constxmlChar*)prefix URI:(constxmlChar*)URI  
  3. {  
  4.     flag=0;//標志歸零  

//解析元素體時觸發

  1. - (void)charactersFound:(constxmlChar*)ch  
  2.                     len:(int)len  
  3. {  
  4.     // 取login_status元素體  
  5.     if(flag==1) {  
  6.         NSString*   string;  
  7.         string = [[NSStringalloc] initWithBytes:ch length:len encoding:NSUTF8StringEncoding];  
  8.         [_rootsetObject:string forKey:@"login_status"];  
  9.         NSLog(@"login_status:%@",string);  
  10.     }  
  11. }  
  12.  
  13. @end 

接下來,改造我們的異步請求操作類URLOperation。首先在interface中增加

兩個變量:

  1. xmlParserCtxtPtr  _parserContext;//Xml解析器指針  
  2. BaseXmlParser* baseParser;//Xml解析器 

其中第1個變量(一個結構體)的聲明顯得有點奇怪,似乎是跟第2個變量混淆了。這是因為libxml是一個c函數庫,其函數調用仍然使用一種面向結構的編程風格。所以我們在后面還會看到一些結構體似的變量。

另外,把_data成員的類型從NSMutableData改變為NSMutableDictionary,并把它配置為屬性 ,因為我們的請求結果應當被xml解析器解析為dictionary了:

  1. @property(nonatomic,retain) NSDictionary *data; 

當然,記住為它提供訪問方法:

  1. @synthesizedata=_data

然后,更改initWithURLString構造方法,為其增加一個名為xmlParser的參數:

  1. - (id)initWithURLString:(NSString*)url xmlParser:(BaseXmlParser*)parser{  
  2.     if(self= [superinit]) {  
  3.         baseParser=[parser retain];  
  4.         NSLog(@"%@",url);  
  5.         _request= [[NSURLRequestalloc] initWithURL:[NSURLURLWithString:url]];//[[NSURLRequest requestWithURL:[NSURL URLWithString:url]]retain];  
  6.         //構建gb2312的encoding  
  7.        enc=CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);  
  8.         _data= [[NSMutableDatadata] retain];  
  9.     }  
  10.     returnself;  

在start方法中,我們可以這樣創建一個xml解析器指針:

  1. // 創建XML解析器指針  
  2. _parserContextxmlCreatePushParserCtxt(&_saxHandlerStruct, baseParser, NULL, 0, NULL);  
  3.           
  4. 注意第2個參數就是具體實現了sax解析的xml解析器。這個解析器對象是通過構造函數“注入”的。  
  5.  
  6. 而***個參數是一個結構體指針xmlSAXHandler 結構體,這個結構體我們定義為靜態變量(注意把定義放在@implementation⋯⋯@end之外):  
  7.  
  8. //libxml的xmlSAXHandler結構體定義,凡是要實現的handler函數都寫在這里,不準備實現的用null代替。一般而言,我們只實現其中3個就夠了  
  9.  
  10. staticxmlSAXHandler _saxHandlerStruct = {  
  11.     NULL,            /* internalSubset */  
  12.     NULL,            /* isStandalone   */  
  13.     NULL,            /* hasInternalSubset */  
  14.     NULL,            /* hasExternalSubset */  
  15.     NULL,            /* resolveEntity */  
  16.     NULL,            /* getEntity */  
  17.     NULL,            /* entityDecl */  
  18.     NULL,            /* notationDecl */  
  19.     NULL,            /* attributeDecl */  
  20.     NULL,            /* elementDecl */  
  21.     NULL,            /* unparsedEntityDecl */  
  22.     NULL,            /* setDocumentLocator */  
  23.     NULL,            /* startDocument */  
  24.     NULL,            /* endDocument */  
  25.     NULL,            /* startElement*/  
  26.     NULL,            /* endElement */  
  27.     NULL,            /* reference */  
  28.     charactersFoundHandler, /* characters */  
  29.     NULL,            /* ignorableWhitespace */  
  30.     NULL,            /* processingInstruction */  
  31.     NULL,            /* comment */  
  32.     NULL,            /* warning */  
  33.     NULL,            /* error */  
  34.     NULL,            /* fatalError //: unused error() get all the errors */  
  35.     NULL,            /* getParameterEntity */  
  36.     NULL,            /* cdataBlock */  
  37.     NULL,            /* externalSubset */  
  38.     XML_SAX2_MAGIC,  /* initialized 特殊常量,照寫*/  
  39.     NULL,            /* private */  
  40.     startElementHandler,    /* startElementNs */  
  41.     endElementHandler,      /* endElementNs */  
  42.     NULL,            /* serror */  
  43. }; 

機構體中填入了我們準備實現的3個方法句柄,因此我們還應當定義這3個方法。由于結構體是靜態的,只能訪問靜態成員,所以這3個方法也是靜態的:

  1. //3個靜態方法的實現,其實是調用了參數ctx的成員方法,ctx在_parserContext初始化時傳入  
  2. staticvoidstartElementHandler(  
  3.                                 void* ctx,  
  4.                                 constxmlChar* localname,  
  5.                                 constxmlChar* prefix,  
  6.                                 constxmlChar* URI,  
  7.                                 intnb_namespaces,  
  8.                                 constxmlChar** namespaces,  
  9.                                 intnb_attributes,  
  10.                                 intnb_defaulted,  
  11.                                 constxmlChar** attributes)  
  12. {  
  13.     [(BaseXmlParser*)ctx  
  14.      startElementLocalName:localname  
  15.      prefix:prefix URI:URI  
  16.      nb_namespaces:nb_namespaces  
  17.      namespaces:namespaces  
  18.      nb_attributes:nb_attributes  
  19.      nb_defaulted:nb_defaulted  
  20.      attributes:attributes];  
  21. }  
  22.  
  23. staticvoidendElementHandler(  
  24.                               void* ctx,  
  25.                               constxmlChar* localname,  
  26.                               constxmlChar* prefix,  
  27.                               constxmlChar* URI)  
  28. {  
  29.     [(BaseXmlParser*)ctx  
  30.      endElementLocalName:localname  
  31.      prefix:prefix  
  32.      URI:URI];  
  33. }  
  34.  
  35. staticvoidcharactersFoundHandler(  
  36.                                    void* ctx,  
  37.                                    constxmlChar* ch,  
  38.                                    intlen)  
  39. {  
  40.     [(BaseXmlParser*)ctx  
  41.      charactersFound:ch len:len];  

其實這3個靜態方法只是調用了超類BaseXmlParser的成員方法, 他的具體類型依賴于ctx的注入類型, 也就是說,這里的ctx可以是任何BaseXmlParser的子類。 實際使用中,我們應該注入其子類, 從而可以根據不同的情況為URLOperation“注入”不同的解析器,實現解析不同的xml文件的目的 。

現在,需要把解析器應用到NSURLConnection的委托方法中(這里省略了部分代碼,只列出了新增加的部分):

  1. #pragma mark NSURLConnection delegate Method  
  2.  
  3. // 接收到數據(增量)時  
  4. - (void)connection:(NSURLConnection*)connection  
  5.     didReceiveData:(NSData*)data{  
  6.     // 使用libxml解析器進行xml解析  
  7.     xmlParseChunk(_parserContext, (constchar*)[databytes], [datalength], 0);  
  8.          ⋯⋯  
  9. }  
  10.  
  11. // HTTP請求結束時  
  12.  
  13. - (void)connectionDidFinishLoading:(NSURLConnection*)connection {  
  14.              if(baseParser!=nil&& baseParser!=NULL){  
  15.         [selfsetData:[[NSDictionaryalloc] initWithDictionary:[baseParsergetResult]]];  
  16.     }else{  
  17.         NSLog(@"baseparser is nil");  
  18.     }  
  19.     // 添加解析數據(結束),注意***一個參數termindate  
  20.  
  21.     xmlParseChunk(_parserContext, NULL, 0, 1);  
  22.  
  23.    
  24.  
  25.     // 釋放XML解析器  
  26.     if(_parserContext) {  
  27.         xmlFreeParserCtxt(_parserContext), _parserContextNULL;  
  28.     }  
  29.  
  30. ⋯⋯  
  31. }  
  32.  
  33. -(void)connection: (NSURLConnection*) connection didFailWithError: (NSError*) error{  
  34.  
  35.     // 釋放XML解析器  
  36.     if(_parserContext) {  
  37.         xmlFreeParserCtxt(_parserContext), _parserContextNULL;  
  38.     }  
  39. }  
  40. @end 

接下來,在“登錄”按鈕中代碼也要做相應的修改,因為URLOperation的構造函數要求傳遞一個具體的xml解析器對象:

  1. //構造xmlparser  
  2. DLTLoginParser* parser=[[DLTLoginParseralloc]init];  
  3. URLOperation* operation=[[URLOperationalloc ]initWithURLString:url xmlParser:parser];  
  4. [parser release]; 

然后,在接收變更通知方法中打印解析結果:

  1. URLOperation* ctx=(URLOperation*)context;  
  2. NSLog(@"%@",[ctx data]); 

后臺打印結果:

  1. {  
  2.     items =     (  
  3.                 {  
  4.             name = "/U4e91/U7535/U4f01/U4fe1/U901a";  
  5.         },  
  6.                 {  
  7.             name = "/U79fb/U52a8/U8c03/U5ea6";  
  8.         },  
  9.                 {  
  10.             name = "/U79fb/U52a8/U62a2/U4fee";  
  11.         }  
  12.     );  
  13.     "login_status" = true;  

小結:iPhone開發中使用NSOperation實現異步下載的內容介紹完了,希望通過本文的學習能對你有所幫助!

責任編輯:zhaolei 來源: 論壇
相關推薦

2011-08-08 13:50:29

iPhone開發 NSOperatio 多線程

2011-08-15 15:26:20

iPhone開發CocoaXML

2011-08-08 10:42:46

iPhone UITableVie 分頁

2011-07-20 14:53:28

iPhone NSLocalize 國際化

2011-08-11 13:26:30

iPhoneNSLocalized

2011-08-17 13:27:08

iPhone游戲開發objective-c

2021-03-22 08:45:30

異步編程Java

2011-08-17 14:57:31

iPhone應用視頻播放

2015-06-16 11:06:42

JavaCompletable

2024-02-07 11:44:20

NestJSRxJS異步編程

2013-07-15 15:12:40

iOS多線程NSOperationNSOperation

2009-02-24 11:05:07

ibmdwiphonegoogle

2010-10-18 13:16:24

GalleryAndroid

2021-01-19 05:30:55

C# 8異步流IEnumerable

2018-03-26 14:25:55

KubernetesSkaffold命令

2012-04-19 12:58:26

TitaniumJSS

2011-07-27 11:19:33

iPhone UITableVie

2011-07-26 14:18:20

2011-07-28 10:11:54

iPhone開發 備忘

2011-08-18 16:24:44

iPhone開發圖片
點贊
收藏

51CTO技術棧公眾號

美日韩一级片在线观看| 日韩精品久久久久久久软件91| www.欧美.com| 欧美精品激情在线观看| 中文字幕无码人妻少妇免费| 手机在线观看av| 国产成人在线看| 久久久www免费人成精品| 97久久精品人人澡人人爽缅北| 日韩av资源在线| 亚洲色图狠狠干| 香蕉久久夜色精品国产更新时间| 欧美日韩亚洲成人| 日本成人看片网址| 国产日产亚洲系列最新| 亚洲看片免费| 日韩一区二区福利| 99久久久无码国产精品性波多| 最近高清中文在线字幕在线观看1| 国产99一区视频免费| 国语自产精品视频在线看抢先版图片 | 国产成人亚洲精品狼色在线 | а√天堂在线官网| av午夜一区麻豆| 国产精选久久久久久| 肉色超薄丝袜脚交69xx图片| 嫩呦国产一区二区三区av| 精品久久久一区二区| 尤物一区二区三区| 青青草超碰在线| 亚洲女同同性videoxma| www.亚洲一区| 丰腴饱满的极品熟妇| 成人欧美在线| 91网站在线观看视频| 色综合天天综合网国产成人网| 最近中文字幕无免费| 精品久久毛片| 狠狠躁天天躁日日躁欧美| 一区二区三区欧美在线| 亚洲av片在线观看| 国产高清一区日本| 成人字幕网zmw| 中国老头性行为xxxx| 亚洲欧美伊人| 日韩在线观看视频免费| 中国毛片在线观看| 大型av综合网站| 7777精品伊人久久久大香线蕉| 最近免费中文字幕中文高清百度| 波多野结衣精品| 中文字幕中文字幕在线一区| 欧美日韩一区二| 色呦呦中文字幕| 国产精品影视网| 成人免费直播live| 亚洲 欧美 成人| 欧美日一区二区三区在线观看国产免| 中文字幕日韩欧美| 日本精品在线观看视频| 亚洲电影男人天堂| 亚洲精品一区av在线播放| 国产+高潮+白浆+无码| 影音先锋欧美激情| 91成人免费网站| 老太脱裤让老头玩ⅹxxxx| 麻豆视频在线免费观看| 中文字幕欧美日韩一区| 视频一区二区在线| a黄色在线观看| 国产女主播一区| 欧美人xxxxx| 国产www.大片在线| 国产精品三级电影| 欧美一级爱爱| 黄色小视频在线观看| 久久精品网站免费观看| 欧美日韩喷水| 色wwwwww| 久久蜜桃av一区二区天堂| 久久精品国产一区二区三区日韩 | 欧美激情亚洲综合| 亚洲一区二区伦理| 国产成人啪精品视频免费网| 亚洲乱码国产乱码精品| 日韩精品欧美精品| 国产日韩综合一区二区性色av| 日韩www视频| 免费萌白酱国产一区二区三区| 亚洲国产精品免费| 熟妇高潮一区二区| 亚洲警察之高压线| 日韩专区在线播放| 国产一级做a爱免费视频| 亚洲美女一区| 国产精品久久久久国产a级| 中文字幕在线观看国产| 韩国v欧美v亚洲v日本v| 成人福利在线视频| 亚洲第一第二区| 久久影院视频免费| 伊人久久av导航| 午夜老司机在线观看| 亚洲最大成人综合| 久久久噜噜噜www成人网| 韩国三级一区| 欧美一级日韩免费不卡| 日本一卡二卡在线| 国语产色综合| 色综合久久88色综合天天看泰| 全部毛片永久免费看| 久久精品国产一区二区| 成人性生交大片免费看视频直播| 亚洲精品久久久久久无码色欲四季 | 成人精品一区二区三区| 特黄视频在线观看| 国产精品伦理一区二区| 69sex久久精品国产麻豆| 国产精品扒开腿做爽爽爽视频软件| 在线91免费看| 丰满少妇一区二区三区| 午夜影院欧美| 日本韩国在线不卡| 国产精品久久久久久无人区| 91一区二区三区在线观看| 亚洲最新免费视频| av毛片午夜不卡高**水| 国产精品久久久久久久久久免费看| 欧美少妇一区二区三区| 春暖花开亚洲一区二区三区| 精品日本一线二线三线不卡| 久久久国产一级片| 国产亚洲福利| 91久久精品一区二区别| 91精品国产91久久久久游泳池 | 亚洲丁香婷深爱综合| 免费污网站在线观看| 综合天堂av久久久久久久| 国产精品第一第二| 亚洲aaa在线观看| 亚洲综合图片区| 8x8x成人免费视频| 精品国产精品| 国产91在线播放九色快色| 黄色三级网站在线观看| 国产精品妹子av| 亚洲乱码国产一区三区| 日韩激情网站| 久久久视频精品| 国产aⅴ爽av久久久久成人| 欧美极品美女视频| 一级特黄性色生活片| 一区二区三区日本久久久| 91精品国产高清| 欧美亚洲精品在线观看| 亚洲一区av在线| 久久久久亚洲av片无码v| 日韩一区三区| 国产精品成人久久久久| 成人午夜免费福利| 亚洲国产精品影院| 国模无码视频一区| 精品电影一区| 国产偷久久久精品专区| 伊人在我在线看导航| 欧美一区二区三区思思人| 国产成人自拍网站| 国产中文字幕精品| 蜜臀av.com| 这里视频有精品| 美女av一区二区| 国产成人精品毛片| 亚洲精品国产一区二区精华液| 手机精品视频在线| 国产欧美日韩视频在线| 日韩免费精品视频| 国产视频二区在线观看| 在线观看亚洲专区| 欧美另类69xxxx| 久久精品国产99| 公共露出暴露狂另类av| 免费欧美网站| 97国产真实伦对白精彩视频8| 色欲av永久无码精品无码蜜桃| 午夜成人免费电影| 人妻视频一区二区| 久久99国产精品尤物| 中文字幕欧美日韩一区二区三区| 久久青草视频| 欧美激情久久久久| 青青草超碰在线| 欧美日韩视频在线一区二区| 又色又爽的视频| 国产美女在线观看一区| 日本大片免费看| 欧美xxxx在线| 国产精品专区h在线观看| 国产成人l区| 91精品黄色片免费大全| 日韩女同强女同hd| 久久女同精品一区二区| 中文字幕永久有效| 红桃视频亚洲| 国产一区二区自拍| 乱人伦视频在线| 中文日韩在线视频| 亚洲奶汁xxxx哺乳期| 无吗不卡中文字幕| 国产探花视频在线播放| 麻豆精品视频在线观看| h无码动漫在线观看| 精品久久91| 春色成人在线视频| 日韩高清中文字幕一区二区| 久久不射热爱视频精品| 欧美精品少妇| 欧美日韩国产精选| 欧美成人777| 国产日韩精品一区二区三区在线| 可以看的av网址| 日本在线播放一区二区三区| 国产又粗又硬又长| 伊人久久大香线蕉av不卡| 91在线免费观看网站| 香蕉久久免费电影| 欧美国产亚洲视频| 网友自拍视频在线| 日韩www在线| 国产精品久久777777换脸| 欧美性猛xxx| 国产精品18p| 亚洲欧美激情一区二区| ass精品国模裸体欣赏pics| 国产精品66部| 熟妇人妻va精品中文字幕| 亚洲视频高清| 一区二区三区一级片| 欧美色图在线播放| 99久久国产免费免费| 欧美亚洲福利| 欧美一级电影在线| 欧美大胆的人体xxxx| www.日韩免费| 国产在线自天天| 精品偷拍一区二区三区在线看| www.日韩高清| 91精品视频网| 国产99免费视频| 亚洲国产日韩a在线播放| 免费在线观看一级片| 免费看欧美美女黄的网站| 亚洲一区精彩视频| 欧美一二区在线观看| 欧美日韩在线精品| 台湾亚洲精品一区二区tv| 国产精品三区www17con| 欧州一区二区三区| 亚洲一区二区自拍| 国产亚洲字幕| av一区观看| 在线日韩三级| 国产精品久久久久久久午夜| 欧美va在线观看| 国产成人短视频| 欧美日韩视频免费观看| 97精品视频在线| aa视频在线观看| 欧美在线一级视频| 狼人综合视频| 国产69精品久久久久9| 久草在线视频资源| 国内精品久久久久久影视8| а√在线中文网新版地址在线| 高清亚洲成在人网站天堂| 成人三级高清视频在线看| 久久天天躁夜夜躁狠狠躁2022| 亚洲国产精品视频在线| 日韩小视频在线观看专区| 国产福利资源在线| 精品国产电影一区二区| 熟妇人妻系列aⅴ无码专区友真希| 日韩精品在线视频| www.中文字幕久久久| 精品国产一区二区三区久久狼黑人 | 中文字幕日韩第一页| 欧美一区二区三区性视频| 日本高清视频免费看| 最新国产精品亚洲| 成人三级小说| 成人av在线天堂| 亚洲第一二三区| www.-级毛片线天内射视视| 亚洲专区欧美专区| 精品国产鲁一鲁一区二区三区| 91亚洲国产成人精品一区二三| 国产在线免费av| 丰满岳妇乱一区二区三区| 国产精品久久久久久免费免熟 | 国产天堂在线播放视频| 国产精品久久久久久亚洲调教| 国产suv精品一区二区四区视频| 色综合久久久久久久久五月| 亚洲片区在线| 亚洲天堂一区二区在线观看| 久久色视频免费观看| 久久久久久久极品内射| 欧美三级一区二区| 亚洲 精品 综合 精品 自拍| 久久久精品国产| 国产精品亚洲成在人线| 欧美日韩国产精品一区二区| 狠狠爱成人网| 精产国品一二三区| 国产精品久久久久影院色老大 | 日本亚洲免费观看| 91av在线免费| 亚洲高清不卡在线| 精品国产va久久久久久久| 色婷婷成人综合| 国产精品久久久久久吹潮| 蜜桃av噜噜一区二区三| 欧美在线精品一区| 亚洲男人天堂av在线| 日本一区二区三区视频视频| 天堂网中文字幕| 亚洲国产欧美久久| heyzo高清国产精品| av蓝导航精品导航| 中文字幕一区二区精品区| 日本黄色福利视频| 国产免费成人在线视频| 免费观看日批视频| 亚洲欧美日韩综合| 亚洲性色av| 久久影院理伦片| 国产精品人人爽人人做我的可爱 | 欧美国产精品v| 无码人妻丰满熟妇区bbbbxxxx | 欧美日本在线观看| 在线中文资源天堂| 国产欧美最新羞羞视频在线观看| 欧美日韩激情在线一区二区三区| 欧美黄色免费影院| 久久男人中文字幕资源站| 日韩电影在线观看一区二区| 亚洲视频自拍偷拍| 欧美影视资讯| 亚洲激情一区二区| 麻豆精品视频在线观看视频| 99久久久免费精品| 欧美一区三区四区| 欧美xxxxhdvideosex| 国产精品自拍首页| 亚洲美女少妇无套啪啪呻吟| jizz欧美性20| 在线免费观看日本一区| 色的视频在线免费看| 成人精品久久久| 国产精品多人| av无码av天天av天天爽| 在线观看国产日韩| 在线观看免费高清完整| 92福利视频午夜1000合集在线观看| 香蕉久久网站| 国产一卡二卡三卡四卡| 日韩欧美在线视频日韩欧美在线视频 | 亚洲伊人伊成久久人综合网| 国产日韩欧美大片| 99国产精品久| 成人免费毛片视频| 日韩综合中文字幕| 91精品国产自产精品男人的天堂| 国产免费黄色av| 国产精品麻豆网站| 亚洲乱码国产乱码精品精软件| 97精品国产97久久久久久春色| 亚洲婷婷伊人| 日韩精品aaa| 精品国产91久久久久久| 999国产在线视频| 成人资源av| 日韩av网站免费在线| 久久久久久久久久久久久久免费看 | 欧美日韩精品在线播放| 在线看av的网址| 国产欧美日韩视频一区二区三区| 日本欧美大码aⅴ在线播放| 中文字幕人妻一区二| 日韩精品福利网站| 高清精品久久| 国产性xxxx18免费观看视频| 国产精品久久久久影院亚瑟| 手机看片国产1024| 成人免费网视频| 久久一综合视频| 精品无码免费视频| 国产一区二区三区视频免费| 秋霞午夜一区二区三区视频| 老司机午夜av| 亚洲国产一区二区在线播放|