2

我想充分了解下面的代碼我在研究iOS的後臺任務後放在一起,我希望一些幫助,瞭解後臺任務執行語法和GCD

我理解基本概念,

首先我們得到應用程序單例,然後我們創建一個塊並向系統註冊後臺任務,然後最後我們異步調度要運行的任務。

因此,這裏是我尋求幫助與片:

  1. 當background_task分配塊,實際塊並沒有我們想它裏面運行的代碼,只有在清理代碼它是完成處理程序,爲什麼?

  2. 我明白dispatch_async基本上啓動一個新的線程,並開始通過塊中的代碼工作,但在這個dispatch_async請求是在哪裏引用background_task?我沒有看到系統如何理解我們想要在dispatch_async請求中執行的代碼與我們之前註冊的background_task有關。

  3. 爲什麼我們需要清理代碼在dispatch_async塊的末尾在background_task的完成處理程序中?

很抱歉,如果這些愚蠢的問題,但我只是不明白的語法,

這裏是代碼中,我拼湊起來:

UIApplication *application = [UIApplication sharedApplication]; //Get the shared application instance 

__block UIBackgroundTaskIdentifier background_task; //Create a task object 

background_task = [application beginBackgroundTaskWithExpirationHandler:^{ //Register background_task  
       [application endBackgroundTask: background_task]; //Tell the system that we are done with the tasks 
       background_task = UIBackgroundTaskInvalid; //Set the task to be invalid 
       //Above code called when endBackgroundTask is called 
      }]; 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
       //Perform your tasks that your application requires 

       [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateText) userInfo:nil repeats:YES]; 
       NSLog(@"\n\nRunning in the background!\n\n"); 

       [application endBackgroundTask: background_task]; //End the task so the system knows that you are done with what you need to perform 
       background_task = UIBackgroundTaskInvalid; //Invalidate the background_task 
      }); 
+0

你想要別人解釋你寫的代碼嗎? – hooleyhoop

+0

@hooleyhoop,不,我希望有人幫助解釋我明確表示我「拼湊」在一起的代碼的更詳細的細節,但感謝您的意見。 – Woodstock

+0

我想知道你是否真的知道你在做什麼。 「後臺任務」是當用戶切換到另一個應用程序時執行的任務,通常應避免。而他們與GCD完全沒有關係,這是關於在後臺線程而不是主線程中執行的任務,而您的應用程序是活動應用程序。 – gnasher729

回答

4

有沒有關係後臺任務標識符以及您在輔助線程中執行的工作。後臺任務代表需要額外的時間運行。就這樣。你需要結束它告訴操作系統你已經完成了你想要做的工作。如果您沒有在可用時間內完成此操作,您的應用將被終止。任務ID只是表示操作系統許可一段時間繼續工作的權限。

您必須在兩個地方清理過期處理程序和異步分派塊的末尾,因爲它們表示兩個不同的事件。在你發送到併發隊列的塊中,你完成了任務,因爲你已經完成了你的工作,你想讓操作系統知道它可以掛起你的應用程序;它不需要終止它。在到期處理程序中,這是您最後一次結束任務以防止終止應用程序的機會。你還沒有完成你的工作,但你已經沒有時間了。如果你沒有在這一點結束後臺任務,操作系統會殺死你的應用程序。

順便說一句,在調度隊列上運行的任務中調度定時器將不起作用。計時器在線程的運行循環中計劃。服務調度隊列的工作線程可以隨時終止,並且在任何情況下都不會運行它們的運行循環。

+0

謝謝你的提出,很有意義,所以如果我只是在沒有首先調用beginBackgroundTaskWithExpirationHandler的情況下調用dispatch_async,我的應用程序會過渡到暫停的權利?感謝你的幫助。 – Woodstock

+0

關於計時器,我能夠像這樣工作:NSTimer * backgroundTimer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(updateText)userInfo:nil repeatats:YES]; [[NSRunLoop currentRunLoop] addTimer:backgroundTimer forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] run]; ---這是正確的嗎? – Woodstock

+0

是的,如果你不請求後臺任務,你的應用程序將被暫停。運行線程的運行循環將允許計時器觸發,但a)計時器在您的應用程序被暫停時不會觸發,因此您不清楚爲什麼要將計時器與此問題相結合; b)如果您沒有結束後臺任務,您的應用將被終止; c)爲了啓動一個計時器而保持線程是非常浪費的。只需在主線上安排它就可以了。如果在'-updateText'方法中完成的工作需要在輔助線程上完成,則在該點處將其分派爲異步。 –

1

只是爲了清楚起見,我設置了後臺任務如下:

@interface myClass() 
    @property (nonatomic, assign) UIBackgroundTaskIdentifier backgroundUpdateTask; 
@end 

- (void)importantStuffToCompleteInBackground 
{ 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 
    ^{ 
     [self beginBackgroundUpdateTask]; 

     // do important background stuff 

     [self endBackgroundUpdateTask]; 
    }); 
} 

- (void)beginBackgroundUpdateTask 
{ 
    self.backgroundUpdateTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 
     [self endBackgroundUpdateTask]; 
    }]; 
} 

- (void)endBackgroundUpdateTask 
{ 
    [[UIApplication sharedApplication] endBackgroundTask:self.backgroundUpdateTask]; 
    self.backgroundUpdateTask = UIBackgroundTaskInvalid; 
} 

根據beginBackgroundTaskWithExpirationHandler: docs其餘的背景時間的情況下,達到0,你沒有完成之前的程序將只簡稱爲通過調用dispatch_async中的endBackgroundTask及時在代碼的重要背景資料塊。

+0

這聽起來很適合我想要做的事......只是無法讓它工作。 「重要」領域的內容仍被稱爲過早。 – TheTC

1

您可能在任何時候都會在您的應用中運行各種長時間運行的後臺任務。有幾種方法可以運行後臺任務。

You could spawn a thread或者你可以使用Grand Central Dispatch to push a block onto a worker queue這看起來那樣簡單..

dispatch_async(queue, ^{ 
[self saveAllUserDataToServer]; 
}); 

,但不要被愚弄,任何涉及多線程的困難和危險。這是一個很大的話題,從你的問題來看,你是否有一個特定的任務需要幫助,並不清楚。

一個這樣的危險是,您的應用程序可以由用戶或系統在任何時候停止。您已經開始的後臺任務可能會完成一半,然後停止。這可能是一場災難,或者只是不方便。

iOS提供了一種不會突然停止這些任務的方法。每當你開始你必須讓系統知道,然後告訴它當任務完成後,像這樣這樣的任務..

- (void)saveAllUserDataToServer { 
    UIBackgroundTaskIdentifier bt = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{}]; 
    ... do the work ... 
    [application endBackgroundTask: background_task]; 
} 

..now你知道它是安全地調用-saveAllUserDataToServer在後臺線程和即使應用半途關閉,它也會運行完成。

需要注意的是,您只有一定的時間限制才能完成任務..如果您花費太長時間,則會調用ExpirationHandler塊,並且您必須在此處正確清理。這是設置代碼更可能看起來像..

__block UIBackgroundTaskIdentifier bt = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ 
    .. task didn't complete within time limit.. do additional cleanup 
    [[UIApplication sharedApplication] endBackgroundTask: background_task]; 
}]; 
+0

謝謝,這是一個很好的閱讀。 – Woodstock

2

這似乎很長時間以來,這個問題被問及並回答。 我只是想在這裏做一個筆記,因爲我有一種感覺,用戶Woodstock(可能是我們很多人)對「後臺線程」和「後臺任務」有點困惑。

  • 「後臺線程」正在考慮在UI線程和後臺線程的上下文中。

  • 「背景任務」正在考慮在任務允許的時間長於正常持續時間的情況下(iOS中爲10秒)。 方法beginBackgroundTaskWithExpirationHandler:告訴iOS您需要更多時間才能完成您在應用程序後臺運行時所做的任何操作(請參閱應用程序的生命週期概念)。此通話結束後,如果您的應用程序背景消失,則在您撥打endBackgroundTask之前仍會獲得CPU時間:或者系統指示它將會過期。

+0

感謝您的額外信息! – Woodstock

+0

@ hoang21感謝您的信息,但正常的後臺進程持續時間是10分鐘而不是10秒。 –

0

我得到以下錯誤,當我的應用程序去的背景,因爲在後臺持續運行進程:

不能endBackgroundTask:沒有後臺任務與標識符9是否存在,或者它可能>已經結束。在UIApplicationEndBackgroundTaskError()中進行調試。

值得慶幸的是,我碰到hooleyhoop的答案,我能得到它周圍有像這樣的代碼示例:

func run() { 
    DispatchQueue(label: "ping").async { 
    let tid = UIApplication.shared.beginBackgroundTask(withName: "ping", expirationHandler: {}) 
    //network code 
    //save data to database code 
    UIApplication.shared.endBackgroundTask(tid)  
    run() //repeat process 
    } 
} 

您還可以使用:

UIApplication.shared.beginBackgroundTask(expirationHandler:{}) 

如果你不」調試時需要一個好名字