iOS NSOperation 非並發執行,iosnsoperation
NSOperation提供了一種物件導向的方法來封裝任務。NSOperation可以單獨執行,也可以放到NSOperationQueue中執行。 NSOperation是虛基類不能直接使用,但Cocoa提供了兩個簡單的子類NSBlockOperation和NSInvocationOperation。NSBlockOperation是將任務封裝到block對象中,NSInvocationOperation 是將任務封裝到selector。 NSBlockOperation 直接使用- (void)startOperation
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"NSBlockOperation Test"); }]; [operation start];} 調用start方法開始執行任務,NSOperation的執行個體方法cancel可以取消正在執行的任務,這比GCD有優勢(GCD中不提供取消任務的功能)。但是這裡並不是我們想像的這麼簡單調用一個cancel方法就夠了,看下面代碼:- (void)startOperation
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
while (1) {
NSLog(@"Test operation cancel funcation");
} }]; [operation start]; [operation cancel];} start方法調用後while迴圈會一直執行,之後調用cancel方法,while迴圈會停止嗎?答案是不會的,因為cancel方法就不會被執行,當前線程一直卡在block任務中。你可能會想我在start方法前設定0.01s延遲後調用執行個體的cancel方法呢?看下面代碼:- (void)startOperation
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
while (1) {
NSLog(@"Test operation cancel funcation");
}
}]; self.operation = operation; [self performSelector:@selector(cancelOperation) withObject:nil afterDelay:0.01f]; [operation start];} - (void)cancelOperation
{
[self.operation cancel];} 實際運行發現cancelOperation方法根本就不會調用,這是為什麼呢?NSOperation本身並不提供多線程的能力,任務是在當前線程中非同步執行,任務執行完成後才執行後面的代碼。cancel方法寫在當前線程,而當前線程一直卡在while迴圈裡,所以cancel方法根本就不會被調用。 既然在同一線程中不能取消死迴圈的任務,那麼,將任務放到後台,在主線程中取消呢?NSOperationQueue提供多線程的能力,將NSOperation任務放到queue中執行。看下面代碼:- (void)startOperation
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
while (1) {
NSLog(@"Test operation cancel funcation");
}
}];
self.operation = operation;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
// [operation cancel];
[self performSelector:@selector(cancelOperation) withObject:nil afterDelay:0.01f];} 這裡的cancelOperation方法和上面一樣省略了,operation任務被添加到NSOperationQueue中在下一個Runloop會被執行,如果緊跟添加後取消,任務就不會被執行。所以我們放到延遲方法中取消operation。這樣總能退出while迴圈了吧!測試發現while迴圈依然在執行,這又是什麼原因了? 在蘋果官方文檔上講解NSOperation有這麼一段話: If an operation were terminated outright, there might not be a way to reclaim resources that had been allocated. As a result, operation objects are expected to check for cancellation events and to exit gracefully when they occur in the middle of the operation.大致意思是任務被中斷了,但分配的記憶體資源有可能回收不了,所以在執行任務前要檢查任務是否被取消了。還有保證任務被取消後釋放分配的記憶體,這點在後面的實現NSOperation子類中要特別注意。 下面這樣才是正確的- (void)startOperation
{
__weak ViewController *wself = self;
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
while (wself.operation.isCancelled == NO) {
NSLog(@"Test operation cancel funcation");
}
}];
self.operation = operation;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation]; [self performSelector:@selector(cancelOperation) withObject:nil afterDelay:0.01f];} 上面使用的都是系統提供的NSOperation的子類,我們也可以自己定義新的子類。NSOperation的任務預設是非並發執行的,唯讀屬性BOOL concurrent(iOS7之後使用BOOL asynchronous)預設返回NO。 將NSOperationQueue之前先弄明白幾個概念:線程、同步、非同步、並發線程是程式執行的最小單元,是進程中的一個實體,是被系統獨立調度和指派的基本單位,同一進程中的多個線程可以並發執行。線程同步是多個線程發生競爭資源,需要依次訪問,線程非同步是多個線程可以同時對同一資源進行訪問執行同步是等待任務完成才能執行後面的代碼,執行非同步是非同步呼叫發出後,接著執行後面的代碼,實際執行調用的過程在後面完成,像performSelector調用。並發執行是指不需要等待任務執行完也能執行後面的代碼。 上面我用到了NSOperationQueue,處於cocoa最上層處理多線程隊列。NSOperationQueue會給加入的每個NSOperation任務開啟一個新的線程,當任務執行完成後銷毀其線程。多個任務是非同步執行的,既為非同步隊列。但可以設定NSOperationQueue的最大同時執行的任務數為1(maxConcurrentOperationCount = 1)來實現同步隊列。非並發的任務添加到NSOperationQueue隊列中也實現了非同步執行。因此如果你需要將NSOperation任務添加到NSOperationQueue隊列中,那就不需要實現NSOperation的並發任務。 關於自訂NSOperation子類和實現concurrent任務會在下一節講。