Grand Central Dispatch、viewWillAppear、viewDidAppear 执行顺序混乱

Posted

技术标签:

【中文标题】Grand Central Dispatch、viewWillAppear、viewDidAppear 执行顺序混乱【英文标题】:Grand Central Dispatch, viewWillAppear, viewDidAppear order of execution confusion 【发布时间】:2011-07-16 11:13:05 【问题描述】:

我在我的标签栏应用中使用 GCD 进行后台下载。

第一步是在-viewWillAppear:做一些后台下载(在加载视图之前设置一些基本数据)。

第二步是到-viewDidAppear:的其余后台下载

由于某种原因,-viewDidAppear: 中的调度块被调用-viewWillAppear: 中的调度块之前。

这只会在第一次加载应用程序后使用 GCD 后台方法切换到选项卡后发生一次。切换到另一个选项卡,然后使用 GCD 后台方法切换回选项卡。第三次(以及随后的所有时间)我切换回来的时候它按预期工作(-viewWillAppear: 先触发然后-viewDidAppear:)。

以下是我的代码摘录(-viewWillAppear:-viewDidAppear:):

-viewWillAppear:

- (void)viewWillAppear:(BOOL)animated 
    DLog(@"viewWillAppear method running");

    [super viewWillAppear:animated];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

        [self setDiskCareerIds:[CareersParser idsFrom:@"disk"]];
        [self setDownloadedCareerIds:[CareersParser idsFrom:@"web"]];


        DLog(@"diskCareerIds after being set in viewWillAppear: %@", [self diskCareerIds])
        DLog(@"downloadedCareerIds after being set in viewWillAppear: %@", [self downloadedCareerIds])

        if ([[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) 

            DLog(@"viewWillAppear: ids equal, loading careers from disk.");
            self.careers = [CareersParser loadCareersFromDisk];

            dispatch_async(dispatch_get_main_queue(), ^

                [self.table reloadData];

                [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];


            );
        

    );

    //[self downloadData];

-viewDidAppear:

- (void)viewDidAppear:(BOOL)animated 
    DLog(@"viewDidAppear method running");

    [super viewDidAppear:animated];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^

        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];

        if (![[self downloadedCareerIds] isEqualToArray:[self diskCareerIds]]) 

            DLog(@"ids not equal, saving careers to disk.");

            dispatch_async(dispatch_get_main_queue(), ^

                [self showLoadingView];

            );

            [CareersParser saveCareersToDisk];
            self.careers = [CareersParser loadCareersFromDisk];
        



        dispatch_async(dispatch_get_main_queue(), ^

            [self.table reloadData];

            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

            [self removeLoadingView];

        );
    );

    //[self download3];

    //[self downloadData];

在Pastie查看调试日志。

【问题讨论】:

【参考方案1】:

好吧,您在第一个块中打印该日志消息(在 viewWillAppear 中安排的那个:)它完成了一堆解析之后,而不是在它实际开始执行时。

问题是全局队列是一个并发队列。因此,即使您首先调度第一个块,它有时落后于与它同时执行的另一个块也就不足为奇了。

一个简单的答案是创建一个串行队列,然后您将确保第一个块在第二个块执行之前完成。这似乎是你想要的,对吧?

【讨论】:

感谢您的回复。但无论哪种方式,方法不应该在 viewWillAppear 中执行 FIRST 吗?我只是不明白 viewDidAppear 之前是如何执行的?但是,我使用serialQueue = dispatch_queue_create("com.acando.viewwillappear", 0); 在 vi​​ewWillAppear 中为调度创建了自己的队列,并在 dispatch_async 方法运行后释放了它dispatch_release(seialQueue);。也对 viewDidAppear 方法做同样的事情,但当然会改变值。但是 viewDidAppear 调度调用仍然在 viewWillAppear 之前运行。 好吧,继续用当前代码更新您的问题,因为您正在使用串行队列,我们​​将看看现在发生了什么。但是对于您最初拥有的并发队列,仅仅因为块 A 在块 B 之前出列,这并不意味着它一定会首先完成执行。如果块 A 需要花费大量时间来解析事物,那么它很容易落后于块 B,因此在块 B 到达其日志语句之后,您会到达块 A 中的那些日志语句。 啊哈,我想我现在明白了。 GCD 调度调用在异步运行时不会“尊重” viewWillAppear 和 viewDidAppear 的执行过程。我将viewWillAppear: 中的GCD 调度调用更改为同步运行,这样它似乎总是在viewDidAppear: 中的GCD 调度调用完成之前完成。 嗯,这并不完全正确。队列仍将是先进先出。所以第一个块将首先开始。但是,这并不意味着它会首先到达日志语句。第二个区块很可能会更快地取得进展。两者将并行执行。我认为让它同步是多余的,很可能会影响你的响应能力。 这只是两个同步下载(不是很多数据,可能10-20kb)。无论如何我将如何异步解决它,因为我需要在其他数据开始下载之前下载这些数据......

以上是关于Grand Central Dispatch、viewWillAppear、viewDidAppear 执行顺序混乱的主要内容,如果未能解决你的问题,请参考以下文章

使用 Grand Central Dispatch 进行文件监控

暂停和恢复 Grand Central Dispatch 线程

swift Grand Central Dispatch(GCD)发送信号量示例

NSOperation 与 Grand Central Dispatch

使用 Grand Central Dispatch (GCD) 创建恰好 N 个线程

核心数据和线程/ Grand Central Dispatch