2011-02-18 80 views
35

是否建議將NSUrlConnection封裝在gcd樣式塊中並在low_priority隊列上運行它?NSURLConnection和盛大的中央調度

我需要確保我的連接不在主線程上發生,並且連接需要異步。我同時需要同時發出多個請求。

如果我去gcd路線,我不知道哪個線程NSUrlConnectionDelegate方法被調用。

NSURLConnection依賴於委託,所以一旦連接完成,無論處理它的包裝類將需要調用它的調用者。我不能確定如何處理所有被調用的各種回調時的連接工作啓動/結束:

- (void)connection:(NSURLConnection *)aConnection didReceiveResponse:(NSURLResponse *)response; 
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)incrementalData; 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection; 

我是否應該打電話同步版本,但包裹在GCD塊?如果我想取消呼叫,請使用'dispatch_suspend'?

dispatch_async(queue,^{ 
     NSString* result = [self mySynchronousHttp:someURLToInvoke]; 
     }); 

// If I need to cancel 
dispatch_suspend(queue); 

回答

53

我建議你在iPhone OS中查看關於網絡應用程序的WWDC會話。

  • WWDC 2010屆207 - 網絡應用程序的iPhone OS,第1部分
  • WWDC 2010屆208 - 網絡應用程序的iPhone OS,第2部分

講師說

「Threads Is Evil™」

for network programming and recommen ded使用RunLoop進行異步網絡編程。使用後臺線程(Grand Central Dispatch Concurrent Queue)進行線程安全數據處理,而不是網絡編程。

順便說一下,塊和大中央調度會議也值得一看。

  • WWDC 2010屆206 - 引入塊和大中央調度iPhone上
  • WWDC 2010屆211 - 簡化iPhone應用程序開發與大中央調度

我寫了一個包裝類異步NSURLConnection的。


AsyncURLConnection.h

#import <Foundation/Foundation.h> 

typedef void (^completeBlock_t)(NSData *data); 
typedef void (^errorBlock_t)(NSError *error); 

@interface AsyncURLConnection : NSObject 
{ 
    NSMutableData *data_; 
    completeBlock_t completeBlock_; 
    errorBlock_t errorBlock_; 
} 

+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock; 
- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock; 
@end 

AsyncURLConnection.m

#import "AsyncURLConnection.h" 

@implementation AsyncURLConnection 

+ (id)request:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock 
{ 
    return [[[self alloc] initWithRequest:requestUrl 
     completeBlock:completeBlock errorBlock:errorBlock] autorelease]; 
} 

- (id)initWithRequest:(NSString *)requestUrl completeBlock:(completeBlock_t)completeBlock errorBlock:(errorBlock_t)errorBlock 
{ 

    if ((self=[super init])) { 
     data_ = [[NSMutableData alloc] init]; 

     completeBlock_ = [completeBlock copy]; 
     errorBlock_ = [errorBlock copy]; 

     NSURL *url = [NSURL URLWithString:requestUrl]; 
     NSURLRequest *request = [NSURLRequest requestWithURL:url]; 
     [NSURLConnection connectionWithRequest:request delegate:self]; 
    } 

    return self; 
} 

- (void)dealloc 
{ 
    [data_ release]; 

    [completeBlock_ release]; 
    [errorBlock_ release]; 
    [super dealloc]; 
} 

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{ 
    [data_ setLength:0]; 
} 

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
{ 
    [data_ appendData:data]; 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection 
{ 
    completeBlock_(data_); 
} 

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 
{ 
    errorBlock_(error); 
} 

@end 

如何使用AsyncURLConnection類。

/* 
* AsyncURLConnection -request:completeBlock:errorBlock: have to be called 
* from Main Thread because it is required to use asynchronous API with RunLoop. 
*/ 

[AsyncURLConnection request:url completeBlock:^(NSData *data) { 

    /* success! */ 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     /* process downloaded data in Concurrent Queue */ 

     dispatch_async(dispatch_get_main_queue(), ^{ 

      /* update UI on Main Thread */ 

     }); 
    }); 

} errorBlock:^(NSError *error) { 

    /* error! */ 

}]; 
+1

這是一個很好的代碼示例,謝謝分享。如果我想在`AsyncURLConnection`中添加一個`cancel`方法來取消內部的`NSURLConnection`,你會如何推薦實現這個方法?添加一個iVar來保存連接,並簡單地調用它的「取消」? – XJones 2011-04-16 03:19:48

1

創建一個運行異步NSURLConnection的併發NSOperation。

+0

所以換句話說,GCD的做法是不是這個合適嗎?我認爲gcd是簡化這種事情的方法。 – 2011-02-22 21:20:43

0

看一看這個代碼塊:

-(void)getDataFromServer 
{ 
    NSDictionary *dicParams = [NSDictionary dictionaryWithObjectsAndKeys: 
          userId, kUserID, 
          pageIndex,kPageIndex, 
          nil]; 

    NSString *yourURL = [self addQueryStringToUrlString:[NSString stringWithFormat:@"%@/%@",_PATH_,apiName] withDictionary:dicParams]; 


    NSString *queue_id = @"_queue_id_"; 
    dispatch_queue_t queue = dispatch_queue_create([queue_id UTF8String], 0); 
    dispatch_queue_t main = dispatch_get_main_queue(); 

    dispatch_async(queue, ^{ 

     NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:yourURL] 
                cachePolicy:NSURLRequestReloadIgnoringCacheData 
               timeoutInterval:60.0]; 
     [theRequest setHTTPMethod:@"GET"]; 
     [theRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"]; 
     [theRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; 

     NSError  *serviceError = nil; 
     NSURLResponse *serviceResponse = nil; 
     NSData *dataResponse = [NSURLConnection sendSynchronousRequest:theRequest 
                returningResponse:&serviceResponse 
                   error:&serviceError]; 

     if(serviceError) 
     { 
      dispatch_sync(main, ^{ 

       // Update your UI 

       if(serviceError.code == -1012){ 
        // Log error 
       }else{ 
        // Log error 
       } 
      }); 
     } 

     else 
     { 
      NSError *jsonError = nil; 

      id dataObject = [NSJSONSerialization JSONObjectWithData:dataResponse 
                  options:kNilOptions 
                   error:&jsonError]; 
      NSMutableArray *arrResponse = (NSMutableArray *)dataObject; 

      dispatch_sync(main, ^{ 

       // Update your UI 
       [yourTableView reloadData]; 
      }); 
     } 
    }); 
} 

+(NSString*)addQueryStringToUrlString:(NSString *)urlString withDictionary:(NSDictionary *)dictionary 
{ 
    NSMutableString *urlWithQuerystring = [[NSMutableString alloc] initWithString:urlString]; 

    for (id key in dictionary) { 
     NSString *keyString = [key description]; 
     NSString *valueString = [[dictionary objectForKey:key] description]; 

     if ([urlWithQuerystring rangeOfString:@"?"].location == NSNotFound) { 
      [urlWithQuerystring appendFormat:@"?%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]]; 
     } else { 
      [urlWithQuerystring appendFormat:@"&%@=%@", [self urlEscapeString:keyString], [self urlEscapeString:valueString]]; 
     } 
    } 
    return urlWithQuerystring; 
} 

+(NSString*)urlEscapeString:(NSString *)unencodedString 
{ 
    CFStringRef originalStringRef = (__bridge_retained CFStringRef)unencodedString; 
    NSString *s = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,originalStringRef, NULL, NULL,kCFStringEncodingUTF8); 
    CFRelease(originalStringRef); 
    return s; 
}