在iOS上使用单独的线程联网

我开发的应用程序是与OS X服务器进行通信的iOS客户端。 这个应用程序的当前版本执行主线程上的所有networking逻辑,这对我想要做的工作很好。

但是,在下一个版本中,我希望networking逻辑更加灵活。 为了这个工作,我想专门给它一个单独的线程,但我不是很确定什么解决scheme适合我的需求。

起初,GCD看起来是一个很好的候选人,但似乎只适合在单独的线程上执行大量的工作。 我想要做的是在一个单独的线程上拥有所有的networking逻辑。 iOS客户端和OS X服务器之间的连接是持久的,所有的数据stream和处理都应该在这个单独的线程上进行。

问题归结为什么方法最适合这种情况?

编辑:为了摆脱任何困惑,我使用的连接使用套接字和NSStream实例。 我不处理连接到远程Web服务器。 换句话说,AFNetworking和ASIHttpRequest不是我的select。

  1. 你可以用runloop创build一个线程(我们称之为NetworkThread),运行下面的代码:

    while (!self.isCancelled) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; [pool release]; } 
  2. 那么你可以使用- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait在NetworkThread上执行您的networking请求select器。

  3. 所有networkingcallback将在NetworkThread上调用,然后在NetworkThread上处理您的响应数据,将最终数据推送到主线程,更新UI。

AFNetworking真棒。 使用块和GCD。 我写了一个NetworkClient类,使得调用它非常简单。 随意使用它或定制它,但我不保证任何东西:)

API密钥是可以删除,如果这不符合您的需求。 我添加它作为一种方法来增加安全性,我所有的networking请求。 我的web服务在返回任何东西之前检查传入的APIKey的有效性。

而且,你可以在你想要完成的事情上相当灵活。 您可以执行同步或asynchronous请求,在发生故障时不会警告用户的请求等。

基本范例用法:

 NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys: @"GetAllParkingLots", @"Command", nil]; [NetworkClient processURLRequestWithURL:nil andParams:params block:^(id obj) { // I check to see what kind of object is passed back, in this case I was expecting an Array if ([obj isKindOfClass:[NSArray class]]) { // Do something with the Array [self processNetworkRequestWithArray:(NSArray *)obj]; } }]; 

NetworkClient.h:

 // // NetworkClient.h // // Created by LJ Wilson on 2/18/12. // Copyright (c) 2012 LJ Wilson All rights reserved. // #import <Foundation/Foundation.h> extern NSString * const APIKey; @interface NetworkClient : NSObject +(void)processURLRequestWithURL:(NSString *)url andParams:(NSDictionary *)params block:(void (^)(id obj))block; +(void)processURLRequestWithURL:(NSString *)url andParams:(NSDictionary *)params syncRequest:(BOOL)syncRequest block:(void (^)(id obj))block; +(void)processURLRequestWithURL:(NSString *)url andParams:(NSDictionary *)params syncRequest:(BOOL)syncRequest alertUserOnFailure:(BOOL)alertUserOnFailure block:(void (^)(id obj))block; +(void)handleNetworkErrorWithError:(NSError *)error; +(void)handleNoAccessWithReason:(NSString *)reason; @end 

NetworkClient.m:

 // // NetworkClient.m // // Created by LJ Wilson on 2/18/12. // Copyright (c) 2012 LJ Wilson All rights reserved. // #import "NetworkClient.h" #import "AFHTTPClient.h" #import "AFHTTPRequestOperation.h" #import "SBJson.h" #warning Set APIKey or set to nil NSString * const APIKey = @"YourAPIKEYGoesHere"; @implementation NetworkClient +(void)processURLRequestWithURL:(NSString *)url andParams:(NSDictionary *)params block:(void (^)(id obj))block { [self processURLRequestWithURL:url andParams:params syncRequest:NO alertUserOnFailure:NO block:^(id obj) { block(obj); }]; } +(void)processURLRequestWithURL:(NSString *)url andParams:(NSDictionary *)params syncRequest:(BOOL)syncRequest block:(void (^)(id obj))block { [self processURLRequestWithURL:url andParams:params syncRequest:syncRequest alertUserOnFailure:NO block:^(id obj) { block(obj); }]; } +(void)processURLRequestWithURL:(NSString *)url andParams:(NSDictionary *)params syncRequest:(BOOL)syncRequest alertUserOnFailure:(BOOL)alertUserOnFailure block:(void (^)(id obj))block { #warning Fix default url // Default url goes here, pass in a nil to use it if (url == nil) { url = @"YourDefaultURLGoesHere"; } NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:params]; [dict setValue:APIKey forKey:@"APIKey"]; NSDictionary *newParams = [[NSDictionary alloc] initWithDictionary:dict]; NSURL *requestURL; AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:requestURL]; NSMutableURLRequest *theRequest = [httpClient requestWithMethod:@"POST" path:url parameters:newParams]; __block NSString *responseString = @""; AFHTTPRequestOperation *_operation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest]; __weak AFHTTPRequestOperation *operation = _operation; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { responseString = [operation responseString]; id retObj = [responseString JSONValue]; // Check for invalid response (No Access) if ([retObj isKindOfClass:[NSDictionary class]]) { if ([[(NSDictionary *)retObj valueForKey:@"Message"] isEqualToString:@"No Access"]) { block(nil); [self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:@"Reason"]]; } } else if ([retObj isKindOfClass:[NSArray class]]) { if ([(NSArray *)retObj count] > 0) { NSDictionary *dict = [(NSArray *)retObj objectAtIndex:0]; if ([[dict valueForKey:@"Message"] isEqualToString:@"No Access"]) { block(nil); [self handleNoAccessWithReason:[(NSDictionary *)retObj valueForKey:@"Reason"]]; } } } block(retObj); } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"Failed with error = %@", [NSString stringWithFormat:@"[Error]:%@",error]); block(nil); if (alertUserOnFailure) { // Let the user know something went wrong [self handleNetworkErrorWithError:operation.error]; } }]; [operation start]; if (syncRequest) { // Process the request syncronously [operation waitUntilFinished]; } } +(void)handleNetworkErrorWithError:(NSError *)error { NSString *errorString = [NSString stringWithFormat:@"[Error]:%@",error]; // Standard UIAlert Syntax UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:errorString delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [myAlert show]; } +(void)handleNoAccessWithReason:(NSString *)reason { // Standard UIAlert Syntax UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"No Access" message:reason delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [myAlert show]; } @end