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);
在 viewWillAppear 中为调度创建了自己的队列,并在 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