iOS 在解除 ViewController 时停止 for 循环内的所有当前处理

标签 ios for-loop uiviewcontroller terminate

我正在使用下面的代码,该代码在嵌入 UITableViewUIViewController 中调用。 它会遍历位置列表,创建 NSURL 并将其传递给 NSXMLParser。一切都按预期进行。

但是,我希望如果用户单击后退按钮,则不仅 UIViewController 被解除,而且用户返回到我之前的 UIViewController已经,但我希望立即终止与 -(void)getInfoNSXMLParser 中发生的 for 循环 相关的处理如果它确实仍在运行。

在大多数情况下,处理都会完成,这不会成为问题,但是,在某些情况下,这可能需要更长的时间,并且我不希望代码在按回键后继续运行,因为不需要数据,因此只会浪费资源,并且由于处理多个 NSURL ,当返回到之前的 UIViewController 时可能会堵塞 UI。

-(void)getInfo{
    NSDictionary *places = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"places" ofType:@"plist"]];
    NSArray *placescc = [[NSArray alloc] initWithArray:places.allKeys];
    __block NSUInteger placewaiting = placescc.count;
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *savedLocs = [[defaults objectForKey:@"savedLocs"]mutableCopy]; //got savedLocs
    NSDictionary *thisLoc = [[savedLocs objectForKey:_LocId]mutableCopy]; //got this Loc

    lastInfoCount = 0; // set default
    if ([[thisLoc objectForKey:@"Infos"]count]){
        lastInfoCount = [[thisLoc objectForKey:@"Infos"]count]; // set saved count if we have one
    }

    for (NSString *place in placescc) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://url.com/%@/%@",place, LocIdString]];
            NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];

            dispatch_async(dispatch_get_main_queue(), ^{
                [parser setDelegate:self];
                [parser setShouldResolveExternalEntities:NO];
                [parser parse];

                placewaiting = placewaiting-1;
                if (placewaiting == 0){

                    [self.tableView reloadData];
                    [self.activityIndicator stopAnimating];
                    lastRefresh = [NSString stringWithFormat:@"%@",[NSDate date]];
                }
            });
        });
    }
}

最佳答案

我建议使用 NSOperationQueue 和 NSBlockOperation 然后你可以取消 dealloc 中的操作(或者可能 viewWillDisappear)以及在 GCD 线程测试 isCanceled 上执行的 block 内取消操作并退出/跳过 UI 更新。但是,您必须确保对 View Controller 的弱引用,否则执行 block 将保留 Controller 。像这样的事情:

// @property(nonatomic,weak) NSBlockOperation *backgroundOperation;

+ (NSOperationQueue*)operationQueue
{
    static dispatch_once_t onceToken;
    static NSOperationQueue *operationQueue;
    dispatch_once(&onceToken, ^{
        operationQueue = [[NSOperationQueue alloc] init];
    });

    return operationQueue;
}

- (NSOperationQueue*)operationQueue
{
    return [[self class] operationQueue];
}

- (void)dealloc
{
    [self.backgroundOperation cancel];
}

- (void)getInfo
{
    NSDictionary *places = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"places" ofType:@"plist"]];
    NSArray *placescc = [[NSArray alloc] initWithArray:places.allKeys];
    __block NSUInteger placewaiting = placescc.count;
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *savedLocs = [[defaults objectForKey:@"savedLocs"]mutableCopy]; //got savedLocs
    NSDictionary *thisLoc = [[savedLocs objectForKey:_LocId]mutableCopy]; //got this Loc

    lastInfoCount = 0; // set default
    if ([[thisLoc objectForKey:@"Infos"]count]){
        lastInfoCount = [[thisLoc objectForKey:@"Infos"]count]; // set saved count if we have one
    }

    // Ensure previous operation finished
    [self.backgroundOperation cancel];

    NSBlockOperation *op = [[NSBlockOperation alloc] init];
    self.backgroundOperation = op;
    __weak NSBlockOperation *weakOp = op;
    __weak typeof(self) weakSelf = self;

    [op addExecutionBlock:^{
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://url.com/%@/%@",place, LocIdString]];
        NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
        if (weakOp && !weakOp.isCancelled)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                [parser setDelegate:weakSelf];
                [parser setShouldResolveExternalEntities:NO];
                [parser parse];

                placewaiting = placewaiting-1;
                if (placewaiting == 0){

                    [weakSelf.tableView reloadData];
                    [weakSelf.activityIndicator stopAnimating];
                    lastRefresh = [NSString stringWithFormat:@"%@",[NSDate date]];
                }
            });
        }
    }];

    [[self operationQueue] addOperation:op];
}

请注意,weakSelf、weakOp 和 backgroundOperation 属性在其引用的对象被释放时将自动为 null,因此如果在 View Controller 关闭后执行 block 中的 UI 更新代码,weakSelf.tableView/activityIndi​​cator 将为 null 并且无操作。在您的情况下,由于大部分工作是同步检索和解析数据,因此不会中止,但上面的代码意味着 View Controller 将立即释放,并且 View Controller 上的 UI 更新将变为无操作。

关于iOS 在解除 ViewController 时停止 for 循环内的所有当前处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23492250/

相关文章:

swift - For循环等待和延迟 - Swift 4

iOS 开发 : UITableView & segues

ios - Swift - 执行 Segue

iphone - UITableView 重新加载数据崩溃

java - 在 For 循环中重新启动 ArrayList

ios - 如何在两个 View Controller 之间共享mapkit的变量?

ios - 在 iOS 中,每个 UIView 都应该有一个 UIViewController 吗?

ios - 苹果开发者账号注册-个人账号-卖家名称

ios - 将 UICollectionView 滚动到节标题 View

linux - 如何在 bash 中循环遍历一系列十进制数?