我有一个TextField,无论什么时候当我输入字符的时候,我appending这个字符到我的url,然后发送一次请求,我现在
需要取消之前的请求。例如当我输入“shampoo”的时候,我会触发7次代理方法,也就是我会触发七次不同的网络请
求,那么有一个问题,这七次发出的请求,响应的顺序可不是你想的按顺序返回的,例如发送的是1234567,那么返回
数据很有可能是1234576,这样导致最后需要的结果不是“7”,而是“6”。那么看看我是如何解决的以及遇到坑的!!!
1.第一种解决方案(失败)
[NSObject cancelPreviousPerformRequestsWithTarget:self];
我看到了这个方法,最终测试的结果是,它只能取消掉还未进行网络请求的方法 如下所示
[self performSelector:@selector(startSearchRefresh) withObject:nil afterDelay:0.5];
在这个delay的0.5秒以内,如果我再次触发该方法,那么,第一个方法就会取消掉之前还在delay的方法,从而达到目的
但是如果到了0.5秒,方法已经进行http请求了呢,已经在请求的过程中了呢,这个方法是根本取消不掉的。
因此,这个方法可以用于用户快速输入的时候后一次操作覆盖前一次操作,对我来说还是失败的方法
2.第二种解决方案(失败)
当我进行网络请求的时候
[manager.operationQueue cancelAllOperations];
获取到AFNetWorking的对象的时候,拿到它的任务队列,然后取消掉之前所有的任务,我不知道是不是版本的问题,
最新的AF(3.0)以上貌似根本没什么用,也有可能是我的插入方式有问题吧
来看看一个老外的实时搜索的代码段,根本没取消掉
- -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;
- {
-
-
-
-
-
- [self.s_tableView setHidden:true];
- [searchProductArray removeAllObjects];
- [self.s_tableView reloadData];
-
- NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:UNACCEPTABLE_CHARACTERS] invertedSet];
- NSLog(@"%@",cs);
-
- NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
- NSLog(@"%@",filtered);
-
- NSLog(@"%lu",(unsigned long)filtered.length);
-
- if (filtered.length) {
- [CustomToastAlert showToastInParentView:self.view withText:@"Please enter valid characters" withDuaration:1.5];
- return NO;
- }
-
-
- searchTextString = [textField.text stringByAppendingString:string];
- NSLog(@"%lu",(unsigned long)[searchTextString length]);
-
-
- NSLog(@"%@",searchTextString);
- int stringLength=[searchTextString length];
- const charchar * _char = [string cStringUsingEncoding:NSUTF8StringEncoding];
- int isBackSpace = strcmp(_char, "\b");
-
- if (isBackSpace == -8) {
- stringLength=[searchTextString length];
- stringLength=[searchTextString length]-1;
- searchTextString=[searchTextString substringToIndex:stringLength];
- NSLog(@"Backspace was pressed");
- NSLog(@"string is %@",searchTextString);
- }
-
-
-
- if(stringLength>=3)
- {
- AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] init];
- manager.responseSerializer = [AFJSONResponseSerializer serializer];
- NSString *urlString=[NSString stringWithFormat:kSearchProductUrl,kBaseUrl];
-
- urlString = [urlString stringByAppendingString:searchTextString];
- urlString = [urlString stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
-
- NSLog(@"%@",searchTextString);
- NSLog(@"%@",urlString);
- [searchProductArray removeAllObjects];
-
-
-
-
- [manager GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject)
- {
- [searchProductArray removeAllObjects];
-
-
- NSLog(@"JSON: %@", responseObject);
- json= responseObject;
- NSLog(@"%@",json);
- NSLog(@"%lu",(unsigned long)[[json valueForKeyPath:@"data"] count ]);
-
- for(int i=0;i<[[json valueForKeyPath:@"data"]count ];i++)
- {
- Product *s_productList=[[Product alloc]init];
- s_productList.SKU_Name=[[json valueForKeyPath:@"data.sku_name"]objectAtIndex:i];
- s_productList.SKU_Id=[[json valueForKeyPath:@"data.sku_id"]objectAtIndex:i];
- s_productList.SKU_Price=[[json valueForKeyPath:@"data.sku_price"]objectAtIndex:i];
- s_productList.SKU_OfferPrice=[[json valueForKeyPath:@"data.sku_offer_price"]objectAtIndex:i];
-
- s_productList.SKU_Currency = RUPEE_SYMBOL;
- s_productList.SKU_AvailableUnit=[[json valueForKeyPath:@"data.sku_available_unit"]objectAtIndex:i];
- s_productList.SKU_OfferDescription= [[json valueForKeyPath:@"data.sku_offer_desc"]objectAtIndex:i];
- s_productList.SKU_ImageUrls=[[json valueForKeyPath:@"data.sku_image_urls"]objectAtIndex:i];
-
-
- [searchProductArray addObject:s_productList];
- NSLog(@"%lu",(unsigned long)[searchProductArray count]);
-
-
- }
- [self.s_tableView setHidden:FALSE];
- [self.s_tableView reloadData];
- NSLog(@"%lu",(unsigned long)[searchProductArray count]);
- if ([searchProductArray count]==0) {
- [CustomToastAlert showToastInParentView:self.view withText:SEARCH_RESULT withDuaration:1.5];
-
-
-
- }
-
-
- }
-
- failure:^(NSURLSessionDataTask *task, NSError *error)
- {
- [CustomToastAlert showToastInParentView:self.view withText:NO_DATA_AVAIL withDuaration:1.5];
- }];
-
-
- }
-
- return YES;
-
- }
3.第三种解决方法,这个才是我最终的解决方案
该方法也是从stackOverFlow上找来的,真的是找死我了
以上面的代码段为例,他是这么操作的
主要精髓在于
第一点:不要initialize a new AFHTTPSessionManager object everytime 一定要把manager用成全局的
第二点:把请求返回的task对象丢进数组,下次触发的时候把遍历数组,把之前的所有任务[task cancel]
-
- - (void)viewDidLoad {
- [super viewDidLoad];
-
-
- self.manager = [[AFHTTPSessionManager alloc] init];
- self.manager.responseSerializer = [AFJSONResponseSerializer serializer];
-
-
- self.arrayOfTasks = [NSMutableArray new];
-
-
-
-
- }
-
-
-
- -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string;{
-
-
-
-
-
-
- if(stringLength>=3){
-
- [self.arrayOfTasks enumerateObjectsUsingBlock:^(NSURLSessionDataTask *taskObj, NSUInteger idx, BOOLBOOL *stop) {
- [taskObj cancel];
- }];
-
-
- [self.arrayOfTasks removeAllObjects];
-
-
- NSURLSessionDataTask *task = [self.manager GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject){
-
- }failure:^(NSURLSessionDataTask *task, NSError *error){
-
- }];
-
-
- [self.arrayOfTasks addObject:task];
- }
- return YES;
- }
注意:
这里的task cancel亲测确实能把网络请求cancel掉,可以看下面的log,记住一点,这里cancel之后不代表就不回调
了,只是会回调到error的那个block里面,各位需要信息的可以测试下,在error打个断点就能调试出来了
-
- [articleInterface.articleArrayTask enumerateObjectsUsingBlock:^(NSURLSessionDataTask *taskObj, NSUInteger idx, BOOLBOOL *stop) {
- DDLogVerbose(@"当前的文章删除前网络任务状态是==================>>>>>%ld",taskObj.state);
- [taskObj cancel];
- DDLogVerbose(@"当前的文章删除后网络任务状态是==================>>>>>%ld",taskObj.state);
- }];
- typedef NS_ENUM(NSInteger, NSURLSessionTaskState) {
- NSURLSessionTaskStateRunning = 0,
- NSURLSessionTaskStateSuspended = 1,
- NSURLSessionTaskStateCanceling = 2,
- NSURLSessionTaskStateCompleted = 3,
- } NS_ENUM_AVAILABLE(NSURLSESSION_AVAILABLE, 7_0);
打印日志 这里的2代表NSURLSessionTaskStateCanceling
ok,圆满解决