我正在编写一个小系统托盘应用程序,它从 API 获取数据并相应地更新其菜单,并且在菜单打开时更新菜单时遇到问题。
我什至不知道从哪里开始,所以让我们从头开始。
我有一个自定义PNLinksLoader
类,其职责是获取数据并解析它:
- (void)loadLinks:(id)sender
{
// instance variable used by the NSXMLParserDelegate implementation to store data
links = [NSMutableArray array];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
if (YES == [parser parse]) {
NSArray *modes = [NSArray arrayWithObject:NSRunLoopCommonModes];
[delegate performSelectorOnMainThread:@selector(didEndLoadLinks:) withObject:links waitUntilDone:NO modes:modes];
}
}];
}
加载程序在应用程序启动时运行一次(完美运行),然后设置一个计时器用于定期刷新:
loader = [[PNLinksLoader alloc] init];
[loader setRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://papyrus.pandanova.com/links"]]];
[loader setDelegate:self];
[loader loadLinks:self];
NSMethodSignature *signature = [loader methodSignatureForSelector:@selector(loadLinks:)];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:loader];
[invocation setSelector:@selector(loadLinks:)];
NSTimer *timer = [NSTimer timerWithTimeInterval:10 invocation:invocation repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
现在每当加载器在服务器上加载新数据时,如果菜单关闭,那么一切正常,我打开菜单,新数据就在那里。
但是,如果菜单在刷新期间恰好打开,则什么也不会发生。如果我关闭并重新打开菜单,那么我可以看到新数据。
我想我错过了关于 RunLoop 的一些东西,但我看不到什么(我对它的理解非常稀疏,因为我实际上是在编写这个小应用程序主要是为了学习 Objective-C)。
编辑
performSelector:withObject:
这里的问题不是在菜单打开时更新菜单,当我使用而不是performSelectorOnMainThread:withObject:waitUntilDone:modes:
在加载程序中时,它实际上有点工作。问题是当我这样做时,在打开菜单时更新菜单时会得到奇怪的结果(关闭菜单时效果很好):
在我的菜单中添加一个NSLog
呼叫会循环fixes
症状,从我在互联网上阅读的内容来看,这可能表明我的线程上有竞争条件(这就是我尝试使用的原因performSelectorOnMainThread
,但我似乎无法理解出去。