2015-06-23 58 views
2

的Xcode 6.3.1 ARC啓用,適用於iOS 8.3的NSTimer在後臺行爲(addTimer :, beginBackgroundTaskWithExpirationHandler :)

我需要幫助理解一個奇怪的行爲,我遇到試圖保持單身共享我的計時器應用程序進入後臺後。

以前我不關心這個NSTimer,因爲它使用後臺位置服務在後臺更新用戶位置。

但是,我想解決的情況是,用戶在後臺或位置更新時拒絕位置更新。位置和計時器爲我的應用程序攜手並進。

我經歷了相當多的在以下主題和網站的職位信息工作...

Apple's - Background Execution

http://www.devfright.com/ios-7-background-app-refresh-tutorial/

http://www.infragistics.com/community/blogs/stevez/archive/2013/01/24/ios-tips-and-tricks-working-in-the-background.aspx

How do I make my App run an NSTimer in the background?

How do I create a NSTimer on a background thread?

Scheduled NSTimer when app is in background?

http://www.raywenderlich.com/92428/background-modes-ios-swift-tutorial

iPhone - Backgrounding to poll for events

出於這種信息,我能夠得到(2)的方法,通過該保持定時器運行-8小時而不iOS的終止後的該應用分配180秒。 (3分鐘)的執行時間,正如蘋果文檔所承諾的那樣。下面是代碼:

AppDelegate.h (不變方法#1或者#2)

#import <UIKit/UIKit.h> 

@interface MFAppDelegate : UIResponder <UIApplicationDelegate> 
{ 
    UIBackgroundTaskIdentifier bgTask; 
    NSInteger backgroundTimerCurrentValue; 
} 

@property (retain, nonatomic) NSTimer *someTimer; 

@end 

AppDelegate.m (方法#1)

< ...

- (void)applicationDidEnterBackground:(UIApplication *)application 
{ 
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 

NSLog(@"applicationDidEnterBackground"); 

backgroundTimerCurrentValue = 0; 

[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; 

self.someTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:true]; 

[[NSRunLoop currentRunLoop] addTimer:self.someTimer forMode:NSRunLoopCommonModes]; 

} 

- (void)timerMethod 
{ 
    NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining]; 

    if (backgroundTimeRemaining == DBL_MAX) 
    { 
     NSLog(@"Background Time Remaining = Undetermined"); 
    } 
    else 
    { 
     NSLog(@"Background Time Remaining = %0.2f sec.", backgroundTimeRemaining); 
    } 

    if (!someBackgroundTaskStopCondition) 
    { 
     [self endBackgroundTask]; 
    } 
    else 
    { 
     nil; 
    } 
} 

...>

AppDelegate.m (方法#2)

< ...

- (void)applicationDidEnterBackground:(UIApplication *)application 
{ 
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 

    NSLog(@"applicationDidEnterBackground"); 

    backgroundTimerCurrentValue = 0; 

    bgTask = UIBackgroundTaskInvalid; 

    self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{ 
     [application endBackgroundTask:bgTask]; 
    }]; 

    self.someTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:true]; 

} 

- (void)timerMethod 
{ 
    NSTimeInterval backgroundTimeRemaining = [[UIApplication sharedApplication] backgroundTimeRemaining]; 

    if (backgroundTimeRemaining == DBL_MAX) 
    { 
     NSLog(@"Background Time Remaining = Undetermined"); 
    } 
    else 
    { 
     NSLog(@"Background Time Remaining = %0.2f sec.", backgroundTimeRemaining); 
    } 

    if (backgroundTimerCurrentValue >= 500) 
    { 
     backgroundTimerCurrentValue = 0; 
     [self.someTimer invalidate]; 
     [[UIApplication sharedApplication] endBackgroundTask:bgTask]; 
    } 
    else 
    { 
     NSLog(@"backgroundTimerCurrentValue: %ld", backgroundTimerCurrentValue); 
     backgroundTimerCurrentValue++; 
    } 
} 

...>

我已經模擬了(2)的方法過夜〜8小時。 (28,800秒),通過調整(backgroundTimerCurrentValue> = 500)的if語句。但爲了重新創建這個問題的結果,我用了500秒。這顯然超過了180秒。這些是從兩種方法的結果:

enter image description here

...後500次迭代self.someTimer,即500秒。

enter image description here

現在,這是偉大的,但這樣做與iPhone 5S測試從Xcode中斷開,等以後奇怪的事情發生......我犯了一個單一視圖的應用程序來檢查自己的價值。一些標籤中的定時器。

長達180秒。標籤用正確的計時器值進行更新。 180秒後。有時候,應用程序似乎仍然可以從打開的應用程序的幻燈片中進行選擇,但是當我點擊屏幕以將應用程序置於前臺時,應用程序會很快重新啓動。

此行爲是Apple承諾的,即180秒後的行爲。並且沒有有效的endBackgroundTask:方法。方法部分是我認爲無效的方法#2中的UIBackgroundTaskIdentifier類型,方法#1中沒有。

所以我的問題如下:

1)什麼是從當iPhone插到我的Mac上運行的Xcode從當它被斷開的不同,系統是否允許發生一定的條件,如這片看似後臺執行?

2)我可以隨時使用定位服務來啓動/停止它們,這樣計時器值會繼續從上次位置更新後過期的時間累積中更新,但任何人都可以開發此代碼以實際運行在斷開連接蘋果手機?

3),如果是在問題2,然後是這樣合法的,當我提出我的應用程序的App Store將被接受?

在此先感謝您的幫助。乾杯!

回答

3

是的,通過調試器運行應用程序將保持它活着,否則將在3分鐘後終止。所以,你顯然不能在生產應用程序中依賴該功能。不,試圖讓應用程序超過這段時間是不合法的(除非您已經請求適當的後臺操作,因爲您的應用程序對於後臺操作具有合法且迫切的需求,例如它是音樂播放器,VOIP,導航應用程序等)。 Apple可能會拒絕任何不符合App Store Review Guidelines的第2.16節的應用程序。

請參閱應用程序編程指南(適用於iOS)的Background Execution章節,以獲得有效後臺操作的更完整討論。

+0

感謝您的快速回復Rob,這就是我的想法;並根據您的經驗和與此主題相關的其他答案的相似性,我馬上將其標記爲答案。所以它必須是應用程序通過插入和使用Xcode調試器而獲得的特殊處理。如果有其他人,知道或關心詳細說明爲什麼,那麼這將是欣賞,只是爲了滿足我的好奇心。 –

相關問題