大家都知道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
@property (nonatomic, assign) int maxNumberOfURLConnection;
@property (nonatomic, assign) id <HTTPConnectionDelegate> delegate;
// 判断指定参数的网络请求是否存在
- (BOOL)requestIsExist:(NSDictionary *)dicParam;
// 指定url是否在请求中
- (BOOL)urlIsRequesting:(NSString *)url;
// 根据URL获取Web数据
// dicParam 可用于回传数据。不得为空
- (BOOL)requestWebDataWithURL:(NSString *)strURL andParam:(NSDictionary *)dicParam;
// 根据URLRequest获取Web数据
// dicParam 可用于回传数据。不得为空
- (BOOL)requestWebDataWithRequest:(NSURLRequest *)request andParam:(NSDictionary *)dicParam;
// 取消网络请求
- (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 receivePartData:(NSData *)partData 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,...)
#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 () {
NSMutableArray *_marrayTaskDic;
}
@property (nonatomic, assign) int numberOfURLConnection;
@end
@implementation HTTPConnection
- (id)init
{
self = [super init];
if (self) {
// Custom initialization.
self.numberOfURLConnection = 0;
self.maxNumberOfURLConnection = MaxNumber_URLConnection;
_marrayTaskDic = [[NSMutableArray alloc] initWithCapacity:5];
}
return self;
}
- (void)dealloc
{
// 清空任务
self.delegate = nil;
[self clearRequest];
//
#if __has_feature(objc_arc)
#else
[_marrayTaskDic release];
[super dealloc];
#endif
}
#pragma mark - Public
// 判断指定参数的网络请求是否存在
- (BOOL)requestIsExist:(NSDictionary *)dicParam
{
for (NSDictionary *dicTask in _marrayTaskDic) {
//
if ([dicParam isEqualToDictionary:dicTask[@"param"]]) {
return YES;
}
}
return NO;
}
// 指定url是否在请求中
- (BOOL)urlIsRequesting:(NSString *)url
{
for (NSDictionary *dicTask in _marrayTaskDic) {
//
NSURLConnection *connect = dicTask[@"connect"];
if ([[[connect originalRequest].URL description] isEqualToString:url]) {
return YES;
}
}
return NO;
}
// 根据URL获取Web数据
// dicParam 可用于回传数据。不得为空
- (BOOL)requestWebDataWithURL:(NSString *)strURL andParam:(NSDictionary *)dicParam
{
if (nil == dicParam) {
return NO;
}
// 实例化NSMutableURLRequest
NSURL *url = [NSURL URLWithString:strURL];
NSMutableURLRequest *mURLRequest = [NSMutableURLRequest requestWithURL:url];
[mURLRequest setCachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData];
[mURLRequest setTimeoutInterval:10.0f];
// 开始请求数据
return [self requestWebDataWithRequest:mURLRequest andParam:dicParam];
}
// 根据URLRequest获取Web数据
// dicParam 可用于回传数据。不得为空
- (BOOL)requestWebDataWithRequest:(NSURLRequest *)request andParam:(NSDictionary *)dicParam
{
// 非主线程取数据,直接同步获取,然后通过协议回调
if ([NSThread currentThread] != [NSThread mainThread]) {
NSURLResponse *response = nil;
NSError *error = nil;
NSData *dataAD = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
[self.delegate httpConnect:self finish:dataAD with:dicParam];
return YES;
}
if (nil == dicParam) {
return NO;
}
// 正在处理或等待处理的任务不再接收
for (NSDictionary *dicTask in _marrayTaskDic) {
//
if ([dicParam isEqualToDictionary:dicTask[@"param"]]) {
HTTPLog(@"任务重复:%@", dicParam);
return NO;
}
}
HTTPLog(@"添加新任务,参数:%@", dicParam);
NSMutableDictionary *mdicTask = [NSMutableDictionary dictionary];
// 设置数据缓存
NSMutableData *mdataCache = [NSMutableData data];
[mdicTask setObject:mdataCache forKey:@"cache"];
// 参数
[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"];
#if __has_feature(objc_arc)
#else
[urlConnection release];
#endif
// 将下载任务保存到数组
[_marrayTaskDic addObject:mdicTask];
[self startURLConnection];
return YES;
}
// 取消网络请求
- (BOOL)cancelRequest:(NSDictionary *)dicParam
{
if (nil == dicParam) {
return NO;
}
// 遍历所有任务
for (int i = 0; i < _marrayTaskDic.count; i++) {
// 查看任务是否相同
NSDictionary *dicTask = _marrayTaskDic[i];
if ([dicParam isEqualToDictionary:dicTask[@"param"]]) {
// 取消网络请求
NSURLConnection *connect = dicTask[@"connect"];
// 未启动的须先启动再取消,不然有内存泄露
if ([TaskStatus_Wait isEqualToString:dicTask[@"status"]]) {
[connect start];
}
else {
self.numberOfURLConnection -= 1;
}
[connect cancel];
// 从任务队列中删除
[_marrayTaskDic removeObjectAtIndex:i];
return YES;
}
}
return NO;
}
// 清空网络请求
- (void)clearRequest
{
// 遍历所有任务
for (NSDictionary *dicTask in _marrayTaskDic) {
NSURLConnection *connect = dicTask[@"connect"];
// 未启动的须先启动再取消,不然有内存泄露
if ([TaskStatus_Wait isEqualToString:dicTask[@"status"]]) {
[connect start];
}
else {
self.numberOfURLConnection -= 1;
}
[connect cancel];
}
// 从任务队列中删除
[_marrayTaskDic removeAllObjects];
}
#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[i];
// 找到网络连接相应的数据字典
if (dic[@"connect"] == connection) {
indexTask = i;
dicTask = dic;
break;
}
}
// 删除失败的任务
if (dicTask) {
// 删除
NSDictionary *dicTempTask = [NSDictionary dictionaryWithDictionary:dicTask];
self.numberOfURLConnection -= 1;
[_marrayTaskDic removeObjectAtIndex:indexTask];
// 启动新任务
[self startURLConnection];
// 通知上层任务失败
if ([self.delegate respondsToSelector:@selector(httpConnect:error:with:)]) {
[self.delegate httpConnect:self error:error with:dicTempTask[@"param"]];
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
HTTPLog(@"网络请求收到响应");
// 找到相应的任务
NSDictionary *dicTask = nil;
for (int i = 0; i < _marrayTaskDic.count; i++) {
NSDictionary *dic = _marrayTaskDic[i];
// 找到网络连接相应的数据字典
if (dic[@"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[@"param"];
// 收到服务器返回的HTTP信息头
if ([self.delegate respondsToSelector:@selector(httpConnect:receiveResponseWithStatusCode:andAllHeaderFields:with:)]) {
[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[i];
// 找到网络连接相应的数据字典
if (dic[@"connect"] == connection) {
dicTask = dic;
break;
}
}
//
if (dicTask) {
// 向缓存中添加数据
NSMutableData *mdataCache = dicTask[@"cache"];
[mdataCache appendData:data];
NSDictionary *dicParam = dicTask[@"param"];
HTTPLog(@"该数据的参数:%@", dicParam);
// 收到部分数据
if ([self.delegate respondsToSelector:@selector(httpConnect:receivePartData:with:)]) {
[self.delegate httpConnect:self receivePartData:data 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[i];
// 找到网络连接相应的数据字典
if (dic[@"connect"] == connection) {
indexTask = i;
dicTask = dic;
break;
}
}
// 删除已经完成的任务
if (dicTask) {
// 删除
NSDictionary *dicTempTask = [NSDictionary dictionaryWithDictionary:dicTask];
self.numberOfURLConnection -= 1;
[_marrayTaskDic removeObjectAtIndex:indexTask];
// 启动新任务
[self startURLConnection];
// 通知上层完成任务
if ([self.delegate respondsToSelector:@selector(httpConnect:finish:with:)]) {
NSData *dataCache = dicTempTask[@"cache"];
NSDictionary *dicParam = dicTempTask[@"param"];
[self.delegate httpConnect:self finish:dataCache with:dicParam];
}
}
}
#pragma mark - ()
- (void)startURLConnection
{
if (self.numberOfURLConnection < self.maxNumberOfURLConnection) {
if (self.numberOfURLConnection < _marrayTaskDic.count) {
// 找到等待状态的任务
for (NSMutableDictionary *mdicTask in _marrayTaskDic) {
if ([TaskStatus_Wait isEqualToString:mdicTask[@"status"]]) {
// 修改状态
[mdicTask setObject:TaskStatus_Run forKey:@"status"];
// 启动
NSURLConnection *urlConnection = mdicTask[@"connect"];
[urlConnection start];
break;
}
}
self.numberOfURLConnection += 1;
}
}
HTTPLog(@"正在处理的网络请求数:%@,等待处理的网络请求:%@",
@(self.numberOfURLConnection), @(_marrayTaskDic.count-self.numberOfURLConnection));
}
@end
这个类有四个接口,后两个是取消网络请求的,不必讨论。前两个是发起网络请求的,用的最多的就是这两个接口了。如果只是通过GET方法从服务器取数据,第一个接口足够了。当然也可以用第二个。如果需要用POST方式向服务器发送数据,得用第二个接口。属性maxNumberOfURLConnection表示同时运行的URL连接数。
第一个接口的第一个参数是URL字符串,第二个参数用来区分网络请求的,如果两次请求的第二个参数一样,第二次请求会被过滤掉。这个参数还有一个作用,就是协议回调函数的参数,用来让上层区分网络请求的。
第二个接口的第一个参数是NSURLRequest类型,用来设置网络请求参数的,比如POST、要发送的数据流、HTTP信息头等。
该类对应的协议就不必谈了,大家能看懂的。该类功能的具体实现,大家自己分析。
有什么问题记得联系我O
最新版本请到Demo中查看:
Demo地址:https://github.com/yjh4866/ExceedMVC