2016-01-22 3 views
4

Итак, на этой странице есть пример о выполнении фона: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html#//apple_ref/doc/uid/TP40007072-CH4-SW1, вот пример:Руководство по программированию Apple для руководства по выполнению фона iOS?

- (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; 
    }]; 

    // Start the long-running task and return immediately. 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     // Do the work associated with the task, preferably in chunks. 

     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }); 

} 

Говорит, что bgTask определяется в классе как переменные. Таким образом, для каждого экземпляра класса (объекта) существует одно свойство bgTask. Если applicationDidEnterBackground нужно было вызывать несколько раз, прежде чем асинхронный блок заканчивается, разве здесь нет опасности состояния гонки? Я имею в виду, что bgTask изменит его значение, и endBackgroundTask будет вызываться при новом значении задачи вместо старого значения?

Не здесь лучшее решение, чтобы сделать это:

__block UIBackgroundTaskIdentifier bgTask; 

перед вызовом beginBackgroundTaskWithName?

+0

вы должны проверить, если bgTask не недействительным и закончить его, прежде чем присваивать ему новое значение. –

+0

if (_bgTask! = UIBackgroundTaskInvalid) [[UAppication sharedApplication] endBackgroundTask: _bgTask]; WEAK_SELF weakSelf = self; _bgTask = [[UAppication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [[UIApplication sharedApplication] endBackgroundTask: weakSelf.bgTask]; weakSelf.bgTask = UIBackgroundTaskInvalid; }]; –

ответ

2

Для каждого объекта есть один экземпляр bgTask, но это находится на AppDelegate, а не на каком-то общем VC или объекте. Таким образом, будет технически только один экземпляр bgTask на работе.

Но это все еще создает проблемы. Поскольку, если этот метод вызывается дважды, он переопределяет значение bgTask. Моя первая мысль заключалась в том, что после выхода из приложения не раз все предыдущие задачи истекали. Но после тестирования выяснилось, что это не так (что хорошо для ИМО). Случилось так, что bgTask был перезаписан (как и ожидалось), и новое значение было передано на первый вызов endBackgroundTask:. Немедленно после этого bgTask установлен в UIBackgroundTaskInvalid, который очищает его, и очищенное значение передается на любые последующие вызовы endBackgroundTask:. Это, очевидно, привело к нечистому завершению, так как не все уникальные идентификаторы были бы закончены, что привело бы к обработчику expiration, выполняющему любые левые над фоновые задачи.

Это, как говорится, я считаю, что ваше предположение об использовании локальной переменной правильное. Если вы попробуете этот код (помещенный в AppDelegateapplicationDidEnterBackground:):

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

NSLog(@"Backgrounded: %@", @(bgTask)); 

// Start the long-running task and return immediately. 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

    // Do the work associated with the task, preferably in chunks. 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     NSLog(@"Done! %@", @(bgTask)); 
     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }); 
}); 

Вы увидите, что каждый местный bgTask присваивается уникальное значение и должным образом завершена через 10 секунд (в соответствии с dispatch_after вызова).

1

Я думаю, что проблема, с которой вы адресуете следующее:
Приложение отправляется в Background состоянии, и IOS называет applicationDidEnterBackground:.
Запущена фоновая задача, которая может занять до нескольких минут.
За это время приложение снова активируется и снова отправляется на задний план, который вызывает снова applicationDidEnterBackground: и запускает другую фоновое задание, в результате чего переменная bgTask будет перезаписана, если она не является блочной переменной. Это верно. Таким образом, bgTask действительно должен быть блочной переменной.
В связи с этой проблемой возникает вопрос, как можно завершить фоновое выполнение, если запущено несколько фоновых задач. Например, как вы можете это сделать, дается here.
Идея состоит в том, чтобы иметь переменную, которая учитывает активные фоновые задачи. Как только все они будут завершены, можно завершить фоновое выполнение.

1

Вы правы, когда вызывается второй раз, applicationDidEnterBackground вызовет проблемы. Но для того, чтобы этот метод был вызван во второй раз, приложение сначала необходимо снова ввести на передний план. Таким образом, решение легко. Просто позвоните обработчик истечения из applicationWillEnterForeground:

- (void)expireBackgroundTask { 
    // Clean up any unfinished task business by marking where you 
    // stopped or ending the task outright. 
    [application endBackgroundTask:bgTask];    
    bgTask = UIBackgroundTaskInvalid; 
} 

- (void)applicationDidEnterBackground:(UIApplication *)application { 

    bgTask = [application beginBackgroundTaskWithName:@"MyTask" expirationHandler:^{ 
     [self expireBackgroundTask]; 
    }]; 

    // Start the long-running task and return immediately. 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     // Do the work associated with the task, preferably in chunks. 

     [application endBackgroundTask:bgTask]; 
     bgTask = UIBackgroundTaskInvalid; 
    }); 

} 

- (void)applicationWillEnterForeground:(UIApplication *)application 
{ 
    [self expireBackgroundTask]; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^