ASIHTTPRequest足够了吗?

        大家都知道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



你可能感兴趣的:(网络,服务器,url,任务,interface)