AFHTTPRequestOperation 的应用

我碰到了一个什么问题?

来说一个场景,打开你的Instagram,如果手机有VPN,请连接上VPN.打开搜索页面,连续输入m,i,k,e.

你会发现,小菊花转了4次.可以想象,客户端向服务端发起了4个请求,搜索的字段分别是"m","mi","mik","mike".

那么问题来了.这四次搜索肯定是并发的,也就是说,客户端同时向服务端发起了四次搜索请求,那么,怎么做到每次返回的结果总是最后一次输入的结果呢?

1
2
3
4
5
6
来看下面这段代码.
for  (int i = 1; i <= 10; i++) {
       [Seller requestSellerWithCompletion:^(id object) {
             NSLog(@ "finished download %d" ,i);
         }];
     }

这个requestSellerWithCompletion方法就是我封装了最简单的一个AFNetworkingOperation请求.

内容如下.

1
2
3
4
5
6
7
8
9
10
11
12
+ (void)requestSellerWithCompletion:(requestFinishedCompletionBlock)successBlock
{
     AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
     [manager GET:kRequestSellerURL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
         NSArray *sellerArray = [MTLJSONAdapter modelsOfClass:[Seller class] fromJSONArray:responseObject[@ "data" ] error:nil];
         if  (successBlock) {
             //  NSLog(@"current operation count is %d",[manager.operationQueue operations].count);
             successBlock(sellerArray);
         }
     } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
     }];
}

好,我们执行以下.console打出的结果如下.

blob.png

对多线程稍有了解的都会知道这结果很正常.并发吗.试想一下,如果你在UITextField的delegate方法里直接对搜索字段执行搜索字段,那么服务端返回的结果肯定是有问题的,因为很有可能你输入的是mike,而返回的是"mi"的搜索结果.

所以,在上面那个for循环中,我们只需要最后一次执行结果.也就是i = 10的执行结果.前9次不管你是执行了也好,还是我中断了也好,我是不需要的.

那么怎么实现呢?

首先,需要否决的是想通过设置maxConcurrentOperationCount = 1解决问题的方案.

原因很简单,request的顺序执行并不能保证response返回也是顺序的.因为网速是时快时慢得.

而且maxConcurrentOperationCount这个参数实际上并不是干这个的.

他的适合的使用场景是为了它的主要意义就是控制连接数,2G网络下一次只能维持一个链接,3G是2个,  

4G和wifi是不限。这个是对应协议的限制。如果超过这个限制发出的请求,就会报超时。

所以你以后封装httpClient的时候可能需要依据网络条件来设置你OperationQueue的这个参数值.

我们应该怎么做?

我又想到了另外一个办法,每次执行request的时候先清空OperationQueue里的所有operation.

也即调用[operationQueue cancelAllOperations]

其实这种想法暴露了我的iOS开发功底不扎实的问题.

来看看苹果文档对这个方法的描述.

Canceling the operations does not automatically remove them from the queue or stop those that are currently executing.

就是即便调用了这个方法也并不能移除正在执行的operation.所以,翻译过来就是,然并卵.

这条路已经死了.

到底应该怎么办?

首先NSOperation有一个Bool值,叫做cancel.这个Bool值并不能控制operation的执行和终止,他只是起一个标记作用.

Yes的时候就是说,我虽然在执行,但是我被cancel掉了.

那么我们每次执行request的时候都把上一个operationcancel掉,然后在completionBlock中判断operation是否cancel,如果cancel那么不返回response的值不就行了么.

我是这么改到.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+ (AFHTTPRequestOperation *)requestSellerWithCompletion:(requestFinishedCompletionBlock)successBlock
{
     AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
     AFHTTPRequestOperation * operation = [manager GET:kRequestSellerURL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
         if  (operation.isCancelled) {
             NSLog(@ "operation is Canceled" );
         } else {
             NSArray *sellerArray = [MTLJSONAdapter modelsOfClass:[Seller class] fromJSONArray:responseObject[@ "data" ] error:nil];
             if  (successBlock) {
               //  NSLog(@"current operation count is %d",[manager.operationQueue operations].count);
                 successBlock(sellerArray);
             }
         }
     } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
     }];
     return  operation;
}

然后在for循环里这么做.

1
2
3
4
5
6
7
8
for  (int i = 1; i <= 10; i++) {
         if  (operation) {
             [operation cancel];
         }
       operation = [Seller requestSellerWithCompletion:^(id object) {
             NSLog(@ "finished download %d" ,i);
         }];
     }

运行一下看看console .

blob.png

正确了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值