首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从异步NSmenu更新NSURLConnection

从异步NSmenu更新NSURLConnection
EN

Stack Overflow用户
提问于 2012-08-02 06:31:14
回答 1查看 692关注 0票数 4

我正在编写一个小的系统应用程序,它从API中获取数据并相应地更新它的菜单,并且在菜单打开时更新它有困难。

我甚至不知道从哪里开始,所以让我们从开始开始。

我有一个定制的PNLinksLoader类,它的职责是获取数据并解析它:

代码语言:javascript
复制
- (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];
        }
    }];
}

加载程序在应用程序启动时运行一次(工作正常),然后为定期刷新设置一个定时器:

代码语言:javascript
复制
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)。

编辑

这里的问题不是在菜单打开时更新它,而是在加载器中使用performSelector:withObject:而不是performSelectorOnMainThread:withObject:waitUntilDone:modes:时,它实际上可以工作。问题是,当我这样做时,我在菜单打开时更新菜单时得到了奇怪的结果(当菜单关闭时,效果非常好):

在我的菜单人口循环中添加一个NSLog调用fixes症状,从我在互联网上看到的信息来看,这可能是我的线程中有种族状况的一个迹象(这就是我尝试使用performSelectorOnMainThread的原因,但我似乎无法弄清楚。

EN

回答 1

Stack Overflow用户

发布于 2012-08-02 22:39:48

问题是,当菜单打开时,当前的运行循环模式不再包含在NSRunLoopCommonModes中:它变成了NSEventTrackingRunLoopMode。

因此,您还必须将计时器添加到此模式的当前运行循环中:

代码语言:javascript
复制
// use "scheduledTimer..." to have it already scheduled in NSRunLoopCommonModes, it will fire when the menu is closed
menuTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fireMenuUpdate:) userInfo:nil repeats:YES];

// add the timer to the run loop in NSEventTrackingRunLoopMode to have it fired even when menu is open
[[NSRunLoop currentRunLoop] addTimer:menuTimer forMode:NSEventTrackingRunLoopMode];

然后在计时方法中,当收到对请求的响应时,调用该方法来更新主线程( UI线程)上的菜单:

代码语言:javascript
复制
[NSURLConnection sendAsynchronousRequest:request
                                   queue:[[NSOperationQueue alloc] init]
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

                           [self performSelectorOnMainThread:@selector(updateMenu) withObject:nil waitUntilDone:NO modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];

                       }];

最后,在update方法中,对菜单数据应用更改,不要忘记要求菜单更新其布局:

代码语言:javascript
复制
[menu removeAllItems];

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"HH:mm:ss"];
[menu addItemWithTitle:[formatter stringFromDate:[NSDate date]] action:nil keyEquivalent:@""];

[menu update];
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11772188

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档