我正在编写一个小系统托盘应用程序,它从 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)。
EDIT
这里的问题不是在菜单打开时更新菜单,它实际上在我使用时有效performSelector:withObject:
代替performSelectorOnMainThread:withObject:waitUntilDone:modes:
在装载机中。问题是,当我这样做时,在打开菜单时更新菜单时会得到奇怪的结果(当菜单关闭时效果很好):
添加一个NSLog
调用我的菜单人口循环fixes
这些症状,从我在互联网上读到的内容来看,这可能表明我的线程上存在竞争条件(这就是为什么我尝试使用performSelectorOnMainThread
,但我似乎无法弄清楚。