NSURLConnection и центральная отправка
Можно ли обернуть NSUrlConnection в блоках стиля gcd и запустить его в очереди low_priority?
Мне нужно убедиться, что мои соединения не происходят в основном потоке, а соединения должны быть асинхронными. Мне также нужно несколько одновременных запросов сразу.
Если я иду по маршруту 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);
Ответы
Ответ 1
Я рекомендую вам видеть сессии WWDC о сетевом приложении в iPhone OS.
- WWDC 2010 Session 207 - Сетевые приложения для iPhone OS, часть 1
- WWDC 2010 Session 208 - Сетевые приложения для iPhone OS, часть 2
Лектор сказал
"Threads Evil ™"
для сетевого программирования и рекомендуется использовать асинхронное сетевое программирование с помощью RunLoop. Используйте фоновый поток (Grand Central Dispatch Concurrent Queue) для потоковой обработки данных, а не для сетевого программирования.
Кстати, также стоит посмотреть блоки и Grand Central Dispatch.
- WWDC 2010 Session 206 - Знакомство с блоками и Grand Central Dispatch на iPhone.
- WWDC 2010 Session 211 - Упрощение разработки приложений для iPhone с помощью Grand Central Dispatch
Я написал класс оболочки для асинхронного 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! */
}];
Ответ 2
Создайте параллельную NSOperation, на которой вы запускаете асинхронное NSURLConnection.
Ответ 3
Посмотрите на этот блок кода:
-(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;
}