Не можете да накарате NSTimer
да работи по този начин, докато приложението ви е във фонов режим. NSTimer
не са механизми в реално време. От официална документация:
Таймерите работят заедно с цикли за изпълнение. За да използвате ефективно таймера, трябва да сте наясно как работят циклите за изпълнение — вижте NSRunLoop и Ръководство за програмиране на нишки. Обърнете внимание по-специално, че циклите за изпълнение поддържат силни препратки към своите таймери, така че не е нужно да поддържате своя собствена силна препратка към таймер, след като сте го добавили към цикъл за изпълнение.
Таймерът не е механизъм в реално време; той се задейства само когато един от режимите на цикъл на изпълнение, към който е добавен таймерът, работи и може да провери дали времето за задействане на таймера е изтекло. Поради различните входни източници, управлявани от типичния цикъл на изпълнение, ефективната резолюция на времевия интервал за таймер е ограничена до от порядъка на 50-100 милисекунди. Ако времето за задействане на таймера възникне по време на дълго извикване или докато цикълът за изпълнение е в режим, който не наблюдава таймера, таймерът не се задейства до следващия път, когато цикълът за изпълнение провери таймера. Следователно действителното време, в което таймерът потенциално се задейства, може да бъде значителен период от време след планираното време за задействане.
Наблягам мой.
Важният извод от това е, че докато вашето приложение е във фонов режим, всеки цикъл на изпълнение, за който вашият таймер би бил планиран, не се изпълнява активно.
Веднага щом приложението ви се върне на преден план, този цикъл на изпълнение се задейства обратно, вижда, че таймерът ви е закъснял, и изпраща съобщението до селектора.
С iOS 7 и следващи, ако искате да извършвате операции във фонов режим, можете да кажете на операционната система, че искате да извършвате фонови извличания.
За да настроим това, първо трябва да кажем на операционната система колко често искаме да извличаме данни, така че в didFinishLaunching...
добавете следния метод:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
application.setMinimumBackgroundFetchInterval(UIApplicationBackgroundFetchIntervalMinimum)
return true
}
Тук можем да подадем произволен интервал от време (например, ако искаме да проверяваме само веднъж на ден). Стойността, която предаваме обаче, определя само минимално време, което трябва да измине между проверките. Няма начин да кажете на операционната система максимално време между проверките.
Сега трябва да внедрим метода, който всъщност се извиква, когато операционната система ни даде възможност да вършим работа във фонов режим:
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// do background work
}
Можем да правим каквото пожелаем в рамките на този метод. Има обаче две уловки.
- Този метод се извиква, докато нашето приложение е във фонов режим. Операционната система ни ограничава до (вярвам) тридесет секунди. След тридесет секунди времето ни изтече.
- Трябва да извикаме
completionHandler()
(или ОС ще си помисли, че сме използвали цялото си време).
completionHandler
, което се предава, приема enum, UIBackgroundFetchResult
. Трябва да го предадем или .Failed
, .NewData
или .NoData
, в зависимост от това какви са действителните ни резултати (този подход обикновено се използва за проверка на сървър за свежи данни).
И така, нашият метод може да изглежда така:
func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// do stuff
if let _ = error {
completionHandler(.Failed)
} else if results.count > 0 {
completionHandler(.NewData)
} else {
completionHandler(.NoData)
}
}
Имайте предвид, че имаме абсолютно нулев контрол върху това колко често операционната система действително ще ни позволи да изпълняваме този код във фонов режим. ОС използва няколко показателя, за да оптимизира изживяването на потребителя.
Мисля, че ако приложението ви докладва .Failed
на манипулатора за завършване, операционната система може скоро да ви даде втори шанс, но ако злоупотребявате с .Failed
, операционната система вероятно може да постави приложението ви в черен списък, за да не използва извличане във фонов режим (и Apple може да откаже вашето приложение).
Ако приложението ви не отчита .NewData
, операционната система ще позволи на приложението ви да работи във фонов режим по-рядко. Не казвам това, защото ви препоръчвам винаги да докладвате .NewData
. Определено трябва да докладвате точно. Операционната система е много умна по отношение на планирането на работа. Ако предавате .NewData
, когато няма нови данни, операционната система ще позволи на приложението ви да работи по-често, отколкото може да е необходимо, което ще изтощи батерията на потребителя по-бързо (и може да доведе до деинсталиране на приложението ви като цяло).
Има обаче и други показатели, свързани с това кога приложението ви ще върши работа във фонов режим. Много малко вероятно е операционната система да позволи на някое приложение да работи във фонов режим, докато потребителят използва активно устройството си, и е по-вероятно да позволи на приложенията да работят във фонов режим, докато потребителят не използва своето устройство. Освен това е по-вероятно операционната система да работи във фонов режим, докато е на WiFi и докато е включена в някакво зарядно устройство.
Операционната система също ще разгледа колко редовно потребителят използва вашето приложение или кога редовно го използва. Ако потребителят използва приложението ви всеки ден в 18:00 ч. и никога по друго време, най-вероятно приложението ви винаги ще получава възможност да работи във фонов режим между 17:30 ч. и 18:00 ч. (точно преди потребителят да използва приложението) и никога през друга част от деня. Ако потребителят много рядко използва вашето приложение, може да минат дни, седмици или месеци между възможностите за работа във фонов режим.
person
nhgrif
schedule
02.05.2015