在iOS中高效地同步数据

时间:2015-01-29 13:58:38

标签: ios grand-central-dispatch

我正在处理应用程序,当应用程序进入后台并且应用程序变为活动状态时,我将数据与服务器同步。因为当我同步到多个设备时,应该在所有设备上更新数据。

所以我就是这样做的,

- (void)applicationDidBecomeActive:(UIApplication *)application
{
     if([[AppManager instance].globalManager activeSyncCounter] == 0)
     {
           [_webService callServerAPI];         
     }
}


- (void)applicationDidEnterBackground:(UIApplication *)application
{
    if([[AppManager instance].globalManager activeSyncCounter] == 0)
     {
           [_webService callServerAPI];         
     }
}

让我解释一下上面的代码

1)我正在调用API来同步我的数据,我遍历for循环来同步我的数据并将其发送到服务器。(所有调用都是异步的)

2)如您所见,我使用了 activeSyncCounter 。我使用过这个,因为用户可以立即打开并退出应用程序,这样就不会再次调用服务器API,直到第一个完成。

现在我怀疑了。

1)首先,当我收到来自服务器的响应时,当数据更多时,通过for循环更新需要时间,并且我的UI变得没有响应。 因此,为了改进,我需要在调度队列中运行for循环代码。 或者他们还有其他更好的方法吗?

此外,当更新完成后,当应用程序进入后台时,当应用程序进入后台时我是否需要调度队列?

2)其次,是他们使用 activeSyncCounter

的任何替代选择

2 个答案:

答案 0 :(得分:1)

你(几乎)从不想在主线程上进行任何同步I / O操作(包括网络操作),所以首先问题,是的,你应该使用异步网络API或将同步操作移到另一个线程 - 调度队列是一种方法。

至于你的第二个问题,我不知道那里确实是一个更好的"更好的"方式,但有不同的方式。我可能建议的一件事是将该检查移到callServerAPI中,以便简化调用该方法的代码。 (即编写一次检查代码,而不是在任何调用服务器API的地方。)

答案 1 :(得分:0)

通过调用完成块来构建网络操作以运行异步并完成是非常重要的。 NSURLConnection提供了一种称为sendAsynchronousRequest:completionHandler:的便捷方法,它就是这样做的。

由于您要完成的某些工作必须在进入后台时完成,因此您的应用需要保持足够长的时间才能完成。 Apple provides a complete article here,但要点是(根据您的情况调整示例代码)......

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{
        // Clean up any unfinished task business by marking where you
        // stopped or ending the task outright.
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // this is your call, modified to use a completion block
    [_webService callServerAPIWithCompletion:^(id result, NSError *error) {
        // set state to indicate that the synch finished...see below
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;        
    }];  
}

所以我认为你有两项工作要做:(1)​​重构网络代码以运行异步并通过调用块完成,(2)提出一个方案来限制运行网络请求的频率

关于后一点,请考虑将NSDate保存到NSUserDefaults。然后你可以问:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDate *lastSynch = [defaults objectForKey:@"MyApp_lastSynch"];

NSTimeInterval sinceLastSynch = -[lastSynch timeIntervalSinceNow];
if (sinceLastCheck < 3600) {
    // we did the last synch less than an hour ago.  skip it for now
} else {
   // do it
}

在同步的完成块中,记下您完成的当前时间:

 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSDate date] forKey:@"MyApp_lastSynch"];
[defaults synchronize];

编辑 - 以下是网络代码在使用块时的外观:

- (void)pushDataWithCompletion:(void (^)(BOOL, NSError))completion;

将所有这些放在一起,您的应用委托代码可能看起来最好:

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // don't bother if we did this recently (60 sec in this e.g.
    if ([self timeSinceLastPushData] < 60) return;

    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // this is your call, modified to use a completion block
    [pushDataWithCompletion:^(BOOL success, NSError *error) {
        if (success) {
             NSLog(@"success.  right down the time");
             [self pushDataComplete];
        } else {
            NSLog(@"error %@, but we'll still tell os that we're done", error);
        }
        [application endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;        
    }];  
}

- (void)pushDataComplete {
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:[NSDate date] forKey:@"MyApp_lastSynch"];
    [defaults synchronize];
}

- (NSTimeInterval)timeSinceLastPushData {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDate *lastSynch = [defaults objectForKey:@"MyApp_lastSynch"];

    return -[lastSynch timeIntervalSinceNow];
}