大家都知道ASIHTTPRequest系列的类很好用,功能很强大。但也像NSURLConnection一样,一个网络请求就需要实例化一个对象,而且无法避免重复请求的问题。
我个人不习惯用ASIHTTPRequest,因为它功能太强大了,函数太多了,而我们平时的项目仅仅是处理基于HTTP协议的网络交互,用ASIHTTPRequest未免太“奢侈”了。更重要的是,它并无法避免网络的重复请求的问题,而且无法控制网络请求数量,所以仅仅使用ASIHTTPRequest还不够。
事实上,用NSURLConnection足够了。下面是我写的封装
HTTPConnection.h
// // HTTPConnection.h // // // Created by Jianhong Yang on 12-1-3. // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. // #import <Foundation/Foundation.h> @protocol HTTPConnectionDelegate; @interface HTTPConnection : NSObject { @private // int _numberOfURLConnection; int _maxNumberOfURLConnection; NSMutableArray *_marrayTaskDic; id <HTTPConnectionDelegate> _delegate; } @property (nonatomic, assign) int maxNumberOfURLConnection; @property (nonatomic, assign) id <HTTPConnectionDelegate> delegate; // 根据URL获取Web数据 - (BOOL)requestWebDataWithURL:(NSString *)strURL andParam:(NSDictionary *)dicParam priority:(BOOL)priority; // 根据URLRequest获取Web数据 - (BOOL)requestWebDataWithRequest:(NSURLRequest *)request andParam:(NSDictionary *)dicParam priority:(BOOL)priority; //取消网络请求 - (BOOL)cancelRequest:(NSDictionary *)dicParam; //清空网络请求 - (void)clearRequest; @end @protocol HTTPConnectionDelegate <NSObject> @optional // 网络数据下载失败 - (void)httpConnect:(HTTPConnection *)httpConnect error:(NSError *)error with:(NSDictionary *)dicParam; // 服务器返回的HTTP信息头 - (void)httpConnect:(HTTPConnection *)httpConnect receiveResponseWithStatusCode:(NSInteger)statusCode andAllHeaderFields:(NSDictionary *)dicAllHeaderFields with:(NSDictionary *)dicParam; // 服务器返回的部分数据 - (void)httpConnect:(HTTPConnection *)httpConnect receiveData:(NSData *)data with:(NSDictionary *)dicParam; // 网络数据下载完成 - (void)httpConnect:(HTTPConnection *)httpConnect finish:(NSData *)data with:(NSDictionary *)dicParam; @end #ifdef DEBUG #define HTTPLOG(fmt,...) NSLog((@"HTTP->%s(%d):"fmt),__PRETTY_FUNCTION__,__LINE__,##__VA_ARGS__) #else #define HTTPLOG(fmt,...) NSLog(fmt,##__VA_ARGS__) #endif
HTTPConnection.m
// // HTTPConnection.m // // // Created by Jianhong Yang on 12-1-3. // Copyright (c) 2012年 __MyCompanyName__. All rights reserved. // #import "HTTPConnection.h" #define MAXNUMBER_URLCONNECTION 10 #define TASKSTATUS_RUN @"Run" #define TASKSTATUS_WAIT @"Wait" @interface HTTPConnection (Private) - (void)startURLConnection; @end @implementation HTTPConnection @synthesize maxNumberOfURLConnection = _maxNumberOfURLConnection; @synthesize delegate = _delegate; - (id)init { self = [super init]; if (self) { // Custom initialization. _numberOfURLConnection = 0; _maxNumberOfURLConnection = MAXNUMBER_URLCONNECTION; _marrayTaskDic = [[NSMutableArray alloc] initWithCapacity:5]; } return self; } - (void)dealloc { //清空任务 [self clearRequest]; // [_marrayTaskDic release]; [super dealloc]; } #pragma mark - #pragma mark Public // 根据URL获取Web数据 - (BOOL)requestWebDataWithURL:(NSString *)strURL andParam:(NSDictionary *)dicParam priority:(BOOL)priority { if (nil == dicParam) { return NO; } NSURL *url = [NSURL URLWithString:strURL]; NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url]; BOOL success = [self requestWebDataWithRequest:request andParam:dicParam priority:priority]; [request release]; return success; } // 根据URLRequest获取Web数据 - (BOOL)requestWebDataWithRequest:(NSURLRequest *)request andParam:(NSDictionary *)dicParam priority:(BOOL)priority { if (nil == dicParam) { return NO; } //正在处理或等待处理的任务不再接收 for (NSDictionary *dicTask in _marrayTaskDic) { // if ([dicParam isEqualToDictionary:[dicTask objectForKey:@"param"]]) { HTTPLOG(@"任务重复:%@", dicParam); //需优先处理且为等待状态 if (priority && [[dicParam objectForKey:@"status"] isEqualToString:TASKSTATUS_WAIT]) { [dicTask retain]; [_marrayTaskDic removeObject:dicTask]; [_marrayTaskDic insertObject:dicTask atIndex:0]; [dicTask release]; } return NO; } } HTTPLOG(@"添加新任务,参数:%@", dicParam); NSMutableDictionary *mdicTask = [[NSMutableDictionary alloc] initWithCapacity:3]; //设置数据缓存 NSMutableData *mdataCache = [[NSMutableData alloc] init]; [mdicTask setObject:mdataCache forKey:@"cache"]; [mdataCache release]; //参数 [mdicTask setObject:dicParam forKey:@"param"]; //状态 [mdicTask setObject:TASKSTATUS_WAIT forKey:@"status"]; //创建HTTP网络连接 NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; [mdicTask setObject:urlConnection forKey:@"connect"]; [urlConnection release]; //保存到数组 if (priority) { [_marrayTaskDic insertObject:mdicTask atIndex:0]; } else { [_marrayTaskDic addObject:mdicTask]; } [mdicTask release]; [self startURLConnection]; return YES; } // 取消网络请求 - (BOOL)cancelRequest:(NSDictionary *)dicParam { if (nil == dicParam) { return NO; } //遍历所有任务 for (int i = 0; i < _marrayTaskDic.count; i++) { //查看任务是否相同 NSDictionary *dicTask = [_marrayTaskDic objectAtIndex:i]; if ([dicParam isEqualToDictionary:[dicTask objectForKey:@"param"]]) { //取消网络请求 NSURLConnection *connect = [dicTask objectForKey:@"connect"]; //未启动的须先启动再取消,不然有内存泄露 if ([TASKSTATUS_WAIT isEqualToString:[dicTask objectForKey:@"status"]]) { [connect start]; } [connect cancel]; //从任务队列中删除 [_marrayTaskDic removeObjectAtIndex:i]; _numberOfURLConnection -= 1; return YES; } } return NO; } // 清空网络请求 - (void)clearRequest { //遍历所有任务 for (NSDictionary *dicTask in _marrayTaskDic) { NSURLConnection *connect = [dicTask objectForKey:@"connect"]; //未启动的须先启动再取消,不然有内存泄露 if ([TASKSTATUS_WAIT isEqualToString:[dicTask objectForKey:@"status"]]) { [connect start]; } [connect cancel]; // _numberOfURLConnection -= 1; } //从任务队列中删除 [_marrayTaskDic removeAllObjects]; } // 获取已经下载到的数据 - (void)loadReceivedDataOf:(NSDictionary *)dicParam into:(NSMutableData *)mdataReceived { for (NSDictionary *dicTask in _marrayTaskDic) { //找到相应的数据 if ([dicParam isEqualToDictionary:[dicTask objectForKey:@"param"]]) { //取出已经下载到的数据 NSData *data = [dicTask objectForKey:@"cache"]; if (data.length > 0) { [mdataReceived setData:data]; break; } } } } #pragma mark - #pragma mark NSURLConnectionDataDelegate - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { HTTPLOG(@"网络请求错误:%@", error); //找到当前失败的任务 int indexTask = 0; NSDictionary *dicTask = nil; for (int i = 0; i < _marrayTaskDic.count; i++) { NSDictionary *dic = [_marrayTaskDic objectAtIndex:i]; //找到网络连接相应的数据字典 if ([dic objectForKey:@"connect"] == connection) { indexTask = i; dicTask = [dic retain]; break; } } //删除失败的任务 if (dicTask) { //删除 _numberOfURLConnection -= 1; [_marrayTaskDic removeObjectAtIndex:indexTask]; //启动新任务 [self startURLConnection]; //通知上层任务失败 NSDictionary *dicParam = [dicTask objectForKey:@"param"]; if ([_delegate respondsToSelector:@selector(httpConnect:error:with:)]) { [_delegate httpConnect:self error:error with:dicParam]; } } [dicTask release]; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { HTTPLOG(@"网络请求收到响应"); //找到相应的任务 NSDictionary *dicTask = nil; for (int i = 0; i < _marrayTaskDic.count; i++) { NSDictionary *dic = [_marrayTaskDic objectAtIndex:i]; //找到网络连接相应的数据字典 if ([dic objectForKey:@"connect"] == connection) { dicTask = dic; break; } } // if ([response isMemberOfClass:NSHTTPURLResponse.class]) { NSHTTPURLResponse *responseHTTP = (NSHTTPURLResponse *)response; NSUInteger statusCode = responseHTTP.statusCode; NSDictionary *dicAllHeaderFields = responseHTTP.allHeaderFields; NSDictionary *dicParam = [dicTask objectForKey:@"param"]; //收到服务器返回的HTTP信息头 SEL receiveResponse = @selector(httpConnect:receiveResponseWithStatusCode:andAllHeaderFields:with:); if ([self.delegate respondsToSelector:receiveResponse]) { [self.delegate httpConnect:self receiveResponseWithStatusCode:statusCode andAllHeaderFields:dicAllHeaderFields with:dicParam]; } } } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { HTTPLOG(@"网络请求收到数据"); //找到相应的任务 NSDictionary *dicTask = nil; for (int i = 0; i < _marrayTaskDic.count; i++) { NSDictionary *dic = [_marrayTaskDic objectAtIndex:i]; //找到网络连接相应的数据字典 if ([dic objectForKey:@"connect"] == connection) { dicTask = dic; break; } } // if (dicTask) { //向缓存中添加数据 NSMutableData *mdataCache = [dicTask objectForKey:@"cache"]; [mdataCache appendData:data]; NSDictionary *dicParam = [dicTask objectForKey:@"param"]; HTTPLOG(@"该数据的参数:%@", dicParam); //收到部分数据 if ([self.delegate respondsToSelector:@selector(httpConnect:receiveProgressChanged:with:)]) { [self.delegate httpConnect:self receiveProgressChanged:mdataCache.length with:dicParam]; } } HTTPLOG(@"网络请求收到数据并处理完成"); } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { HTTPLOG(@"网络请求完成"); //找到当前完成的任务 int indexTask = 0; NSDictionary *dicTask = nil; for (int i = 0; i < _marrayTaskDic.count; i++) { NSDictionary *dic = [_marrayTaskDic objectAtIndex:i]; //找到网络连接相应的数据字典 if ([dic objectForKey:@"connect"] == connection) { indexTask = i; dicTask = [dic retain]; break; } } //删除已经完成的任务 if (dicTask) { //删除 _numberOfURLConnection -= 1; [_marrayTaskDic removeObjectAtIndex:indexTask]; //启动新任务 [self startURLConnection]; //通知上层完成任务 NSData *dataCache = [dicTask objectForKey:@"cache"]; NSDictionary *dicParam = [dicTask objectForKey:@"param"]; if ([_delegate respondsToSelector:@selector(httpConnect:finish:with:)]) { [_delegate httpConnect:self finish:dataCache with:dicParam]; } } [dicTask release]; } #pragma mark - Private - (void)startURLConnection { if (_numberOfURLConnection < _maxNumberOfURLConnection) { if (_numberOfURLConnection < _marrayTaskDic.count) { //找到等待状态的任务 for (NSMutableDictionary *mdicTask in _marrayTaskDic) { if ([TASKSTATUS_WAIT isEqualToString:[mdicTask objectForKey:@"status"]]) { //修改状态 [mdicTask setObject:TASKSTATUS_RUN forKey:@"status"]; //启动 NSURLConnection *urlConnection = [mdicTask objectForKey:@"connect"]; [urlConnection start]; break; } } // _numberOfURLConnection += 1; } } HTTPLOG(@"正在处理的网络请求数:%i,等待处理的网络请求:%i", _numberOfURLConnection, _marrayTaskDic.count-_numberOfURLConnection); } @end
这个类有四个接口,后两个是取消网络请求的,不必讨论。前两个是发起网络请求的,用的最多的就是这两个接口了。如果只是通过GET方法从服务器取数据,第一个接口足够了。当然也可以用第二个。如果需要用POST方式向服务器发送数据,得用第二个接口。属性maxNumberOfURLConnection表示同时运行的URL连接数。
第一个接口的第一个参数是URL字符串,第二个参数用来区分网络请求的,如果两次请求的第二个参数一样,第二次请求会被过滤掉。这个参数还有一个作用,就是协议回调函数的参数,用来让上层区分网络请求的。第三个参数表示是否需要优先处理。
第二个接口的第一个参数是NSURLRequest类型,用来设置网络请求参数的,比如POST、要发送的数据流、HTTP信息头等。第二个、第三个参数的作用跟第一个接口的第二个、第三个参数意义一样。
该类对应的协议就不必谈了,大家能看懂的。该类功能的具体实现,大家自己分析。
有什么问题记得联系我O