在基于文档的MacOS应用程序中,我已经加载了一些大文件(尤其是在应用程序启动时打开的最近文件)。我创建了ProgressController(一个NSWindowController子类)以通知用户窗口正在加载文件。它由我用来管理文档的NSDocument子类的makeWindowControllers
方法分配。它们是在用户打开文件时创建的(特别是在启动时,当用户退出应用程序时显示文档),并且它们在后台队列中异步加载其内容,这在原则上不会影响主线程的性能:
-(instancetype) initForURL:(NSURL *)urlOrNil withContentsOfURL:(NSURL *)contentsURL ofType:(NSString *)typeName error:(NSError *
{
if (self = [super init]){
... assign some variables before loading content...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
[[[NSURLSession sharedSession] dataTaskWithURL:urlOrNil completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
self.content = [[NSMutableString alloc] initWithData:data encoding:encoder];
dispatch_async(dispatch_get_main_queue(), ^(){
[self contentIsLoaded];
});
}] resume];
});
}
return self;
}
在contentIsLoaded中,进度窗口关闭。
- (void) contentIsLoaded
{
... do something ...
[self.progressController close];
}
此行为正常,显示窗口,并在必要时关闭。 当我想更新主队列上此窗口的内容时,会发生问题。我尝试设置NSTimer,但是即使它在主队列中创建,也不会被触发。因此,在MyApplicationDelegate中,我创建了一个GCD计时器,如下所示:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
if (self->timer !=nil) dispatch_source_cancel(timer);
self.queue = dispatch_queue_create( "my session queue", DISPATCH_QUEUE_CONCURRENT);
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(timer, ^{
[self updateProgress];
});
dispatch_resume(timer);
…
}
在MyApplicationDelegate中,updateProgress方法定义为:
- (void) updateProgress
{
dispatch_async(self.queue, ^{
NSLog(@"timer method fired");
dispatch_async(dispatch_get_main_queue(), ^(){
NSLog(@"access to UI fired");
... update window (e.g. NSProgressIndicator)
}
});
if (self.shouldCancelTimer) dispatch_source_cancel(self->timer);
});
}
运行应用程序时,每秒记录一次“ Timer method fired
”。 "timer method fired"
消息(在主队列中)仅记录一次或两次,然后该记录似乎被挂起,直到文件已加载。然后,由于之前已挂起,此丢失的消息连续出现几次。
我错了什么?我认为用于文件加载的后台队列不应影响主队列和UI更新。许多应用程序的行为都是这样,我需要这种行为,因为我的应用程序中的文件(字符串,csv,json)可能是数百兆字节!
答案 0 :(得分:0)
对于初学者来说,您是否研究过NSURLSessionTask的进度报告API?有很多工具可以测量和调用数据负载。您有可能避免进行轮询,而只需在回调这些主界面时更新您的用户界面-全部在主线程上。
实际上,您甚至根本不需要一个。我记得,无论如何,NSURLSession进行的联网操作都是在后台完成的。我敢打赌,您可以删除所有这些内容,而只需使用NSURLSession API中的一些额外的委托回调即可完成此操作。也更简单:)
祝你好运!