如何知道NSOperationQueue何时完成所有请求,以便我可以重新加载我的tableView?

4
我正在使用ASIHTTPRequest和NSOperationQueue从不同的链接下载数据,以在后台线程中进行下载。当请求完成后,我使用ASIHTTPRequest的requestFinished委托方法对其进行解析。我希望在队列中的所有请求完成时更新表格视图中的数据。是否有任何方法可以知道NSOperationQueue何时处理完所有请求?我的意思是队列是否有任何变量,例如“isEmpty”,或任何委托方法,例如“queueDidCompletedAllOperation”?
请帮助。
以下是代码:
//source

@interface SourceModel : NSObject

@property (nonatomic, retain) NSString * link;

@property (nonatomic, retain) NSString * name;

@end


//for rssGroup

@interface CompleteRSSDataModel : NSObject

@property (nonatomic,strong) SourceModel * source;

@property (nonatomic,strong) KissXMLParser * parser;

@property (nonatomic,strong) NSArray * rssArticles;

@end

- (void)viewDidLoad
{
       for (int index=0; index<[rssGroups count]; index++) {

            NSString * urlString = [[[rssGroups objectAtIndex:index] source] link];

            NSURL *url = [NSURL URLWithString:urlString];

            ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request setDelegate:self];

            //set this request's tag to group index of this source(link). See requestFinished for use of this :)
            [request setTag:index];

            [self.queue addOperation:request];
        }

}


- (void)requestFinished:(ASIHTTPRequest *)request {

    NSLog(@"%@",@"RSS Data got from internet successfully :))");


    int groupIndex = [request tag];

    CompleteRSSDataModel * group = [rssGroups objectAtIndex:groupIndex];

    group.parser = [[KissXMLParser alloc]initWithData:[request responseData]];

    if (group.parser == nil) {

        NSLog(@"%@",@"Failed - Error in parsing data :((");
    }

    else {
        NSLog(@"%@",@"Data Parsed successfully :))");

        group.rssArticles = [group.parser itemsInRss];

        //So i want to check here that queue is empty, reload data, but as my information, i don't know any method like hasCompletedAllRequested 

        //if(self.queue hasCompletedAllRequests) {

        //     [self.tableview reloadData];
        //}


    }
}

- (void)requestFailed:(ASIHTTPRequest *)request {

    NSLog(@"%@",@"Error in Getting RSS Data from internet:((");

}

你可能想在这里分享你已经尝试过的内容,例如研究、遇到的问题等。一堆代码并不能帮助任何想要提供帮助的人。 - user457812
也许你是对的。我需要编辑一下问题。谢谢建议。 - iMemon
3个回答

10
如果所有操作都已完成,那么operations数组的计数将为零。
您可以使用键值观察编码来观察NSOperationQueueoperations属性以检查此情况。
要为operations属性设置观察器,请按以下方式执行:
[self.queue addObserver:self forKeyPath:@"operations" options:0 context:NULL];

那么在你的observeValueForKeyPath方法中按照以下方式执行:

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                         change:(NSDictionary *)change context:(void *)context
{
    if (object == self.queue && [keyPath isEqualToString:@"operations"]) {
        if ([self.queue.operations count] == 0) {
            // Do something here when all operations has completed
            NSLog(@"queue has completed");
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object 
                               change:change context:context];
    }
}

iOS 4.0之后,您可以使用属性operationCount,例如self.queue.operationCount == 0,而不是像这样检查[self.queue.operations count] == 0


2

我知道这个问题已经有解决方案了,但是为了未来的读者,如果你正在使用AFNetworking,更具体地说是AFHTTPRequestOperation,你可以像这样做:

NSString *urlPath = [NSString stringWithFormat:@"%@%@", baseUrl, file];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlPath]];

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];

[operation setCompletionBlockWithSuccess:
 ^(AFHTTPRequestOperation *operation, id responseObject) {
   if ([[queue operations] count] ==0) {
       NSNotification * success = [NSNotification notificationWithName:@"TotalDone" object:[NSNumber numberWithBool: YES]];
      [[NSNotificationCenter defaultCenter] postNotification:success];
      queueSize = 0;
   } else {

       //get total queue size by the first success and add 1 back
       if (queueSize ==0) {
           queueSize = [[queue operations] count] +1.0;
       }
       float progress = (float)(queueSize-[[queue operations] count])/queueSize;
       NSNumber * totProgress = [NSNumber numberWithFloat:progress];
       NSNotification * totalProgressNotification = [NSNotification notificationWithName:@"TotalProgress"
                                                                                           object:totProgress];
       [[NSNotificationCenter defaultCenter] postNotification:totalProgressNotification];
   }


} failure:^(AFHTTPRequestOperation *operation, NSError *error) 
        NSLog(@"Error: %@", error);
        }];

这是一个下载器,它将下载添加到NSOperationQueue中,然后通知两个进度条:一个用于文件进度,另一个用于总进度。

希望这可以帮助到您。


0

我想到了几个选项:

  1. 使用 ASINetworkQueue,并设置 queueDidFinishSelector,这样它会在完成时告诉你。这还为你提供了机会,不仅可以更新单个行的 UIProgressView 视图,还可以更新整个下载过程的另一个视图。

  2. 你能否向你的 NSOperationQueue 添加一些东西,简单地调用一个方法来更新你的 UI(当然是在主队列中)?通过将其添加到队列中,直到清除队列,它才会执行。或者在另一个队列中,调用 waitUntilFinished,此时你可以更新你的 UI。

  3. 从你的评论中看,似乎你正在 requestFinished 中更新你的 UI,但你觉得等待所有更新完成可能更好。就我个人而言,我更喜欢逐个请求地更新 UI,这样如果速度慢,你就会得到中间反馈,而不是等待所有内容。当然,这必须做得优雅,但我喜欢边进行操作边更新 UI。不可否认,有些过程不适合这样做。

在这最后一点上,我认为诀窍在于进行这些更新时不会分心。特别是,如果你的UI是一个UITableView,那么你可能 不希望执行tableView.reloadData,因为它会重新加载整个表格。相反,您可能希望检查单元格是否仍在加载(通过cellForRowAtIndexPathUITableView方法,不要与UITableViewController方法tableView:cellForRowAtIndexPath混淆),如果是,则更新该行,例如:

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if (cell)
{
    // you can either update the controls in the cell directly, or alternatively, just
    // call reloadRowsAtIndexPaths to have the tableView use your data source:

    [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                          withRowAnimation:UITableViewRowAnimationFade];
}

你说得对。目前我正在requestFinished中更新UI,但我觉得那不是正确的方式。我认为ASINetworkQueue可能是一个不错的选择。我打算试一下。但我想问一下,它是否像NSOperationQueue一样在后台完成所有下载? - iMemon
是的,在我的代码中有这个功能。此外,它还提供了其他一些好处(例如,如果您使用UIProgressView,您可以为整个排队的作业使用一个),也是如此。 - Rob
目前我正在按照您所说的在requestFinished中更新UI。但问题是,当我有多个请求时,我需要在所有请求完成后更新UI。然后我使用了[self.queue.operation count]消息来解决这个问题。当它变为零时,我可以安全地更新UI。现在我可以使用这种方法显示下载进度。无论如何,谢谢 :) - iMemon
@iMemon,我个人认为在每个requestFinished之后更新UI是很好的。但诀窍在于不要更新整个UI,而只更新适当的行。我已经相应地更新了我的答案。当然,你可以等到最后(在这种情况下,我不建议使用self.queue.operation.count,而是使用setQueueDidFinishSelector来指定队列完成时要调用的方法),但我认为在数据到达时无缝更新UI对用户体验更好。 - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接