2011-02-18 82 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; 
}