如何在 iOS 中使用 AFNetworking 从 REST 接口定期轮询/拉取

Posted

技术标签:

【中文标题】如何在 iOS 中使用 AFNetworking 从 REST 接口定期轮询/拉取【英文标题】:How to periodically poll/pull from a REST interface using AFNetworking in iOS 【发布时间】:2014-05-07 17:59:39 【问题描述】:

我正在我的 iPhone 上构建一个“监控”应用程序。我正在使用 AFNetworking-2.0。我有一个后端服务器公开一个用 Python3/tornado 编写的 RESTful 接口。

根据我所处的ViewController 的级别,我想用不同的查询轮询不同的数据(应用程序的焦点调整查询的焦点)。为了“让它发挥作用”,我设置了以下内容:

#pragma mark - Pull Loop

- (void) forkPull 
    NSString* uri = [NSString stringWithFormat: @"%@/valves",  Site.current.serialID];
    [[HttpConnection current]
        GET: uri
        parameters: @
        success:^(NSURLSessionDataTask* task, id responseObject)
            [Site.current performSelectorOnMainThread: @selector(fromDoc:) withObject:responseObject waitUntilDone:YES];
            NSTimeInterval delay = 60; // default poll period
            // attempt to hone in if we have valid lastTouch info
            if (Site.current.touched != nil) 
                NSDate *futureTick = [Site.current.touched dateByAddingTimeInterval: 65];
                if ([futureTick compare: [NSDate date]] == NSOrderedDescending) 
                    delay = futureTick.timeIntervalSinceNow;
                
            
            [self performSelector: @selector(forkPull) withObject:nil afterDelay:delay];
            NSLog(@"%@ forkPull again in %f", self, delay);
        
        failure:^(NSURLSessionDataTask* task, NSError* error)
            NSLog(@"%@ forkPull error: %@ (uri=%@)", self, error, uri);
            [self performSelector: @selector(forkPull) withObject:nil afterDelay:60];
        
    ];


- (void) stopPull 
    [NSObject cancelPreviousPerformRequestsWithTarget: self];


#pragma mark - View Management

-(void)viewWillAppear:(BOOL)animated
    [super viewWillAppear: animated];
    ....
    [self forkPull]; // start up polling while I'm visible


-(void) viewWillDisappear:(BOOL)animated 
    [super viewWillDisappear:animated];
    [self stopPull]; // I'm going away, so shut down the pull loop?
    ...

基本上,当控制器的视图出现时,它会发送一个 REST 查询(当它异步返回时,它将在 fromDoc: 方法中更新模型;控制器设置了 KVO 关系,这将导致 UI 更改。更新完成后,它可以估计下一次拉取的时间,并使用performSelector:withObject:afterDelay: 安排时间。当另一个控制器占据中心位置时,viewWillDisappear: 方法会尝试停止任何已排队的forkPulls .

虽然这有点工作。我很确定它没有通过“Make it Right”测试。我对所有任务和后台工作的工作方式很幼稚,但在我看来AFNetworking 添加了自己的级别,所以我的stopPull 可能无效。我在NSLog 输出中看到了一些证据,其中似乎不再位于顶部的控制器仍在运行循环。

但我相信其他人以前也做过这种模式。我很想知道如何更好地架构/实现这一点。我正在寻找有人分享他们用于执行半定期 REST 查询的模式,该模式已经过审查并且运行良好。

【问题讨论】:

semi-periodic 是什么意思?想要轮询服务器多少次? 名义上,大约每分钟一次。但是每次查询都会返回一些信息,这让我可以在某种程度上调整它,以确保我轮询接近,但在服务器的下一次更新之后。 【参考方案1】:

使用 Grand Central Dispatch:

@property (strong, nonatomic) dispatch_source_t timer;

- (void)startTimer

    if (!self.timer) 
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    
    if (self.timer) 
        dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 60ull*NSEC_PER_SEC, 10ull*NSEC_PER_SEC);
        dispatch_source_set_event_handler(_timer, ^(void) 
            [self tick];
        );
        dispatch_resume(_timer);
    


- (void)tick

    // Do your REST query here

这将每 60 秒调用一次您的 tick 方法。

要暂停和恢复计时器,请使用 dispatch_suspend 和 dispatch_resume:

dispatch_suspend(self.timer);
dispatch_resume(self.timer);

您可以在以后的任何时间调用dispatch_source_set_timer 来提前安排时间或将它们推迟到以后:

// Fire sooner than 60 seconds, but resume 60s fires after that
unsigned long long delaySeconds = arc4random() % 60;
dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, delaySeconds * NSEC_PER_SEC), 60ull*NSEC_PER_SEC, 10ull*NSEC_PER_SEC);

请参阅 Apple Concurrency Programming Guide 以获取有关此内容的完整文档。

【讨论】:

以上是关于如何在 iOS 中使用 AFNetworking 从 REST 接口定期轮询/拉取的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS 中使用 AFNetworking 从 REST 接口定期轮询/拉取

如何使用 AFNetworking 在 iOS 目标 c 中发出此帖子请求

如何在 iOS Objective-C 中使用 AFNetworking 库下载 .json 文件?

如何在 iOS 中使用 AFNetworking 将数据发送到 php 服务器

如何允许用户在 iOS 中使用 AFNetworking 信任和固定自签名 SSL 证书

如何使用 AFNetworking 3.x 在 ios、objective-C 中使用来自 SOAP webservice 的数据