Objective-C的NSOperation多線程類基本使用指南_IOS

來源:互聯網
上載者:User

NSOperation

一、NSOperation
1.簡介
NSOperation執行個體封裝了需要執行的操作和執行操作所需的資料,並且能夠以並發或非並發的方式執行這個操作。
NSOperation本身是抽象基類,因此必須使用它的子類,使用NSOperation子類的方式有2種:
1> Foundation架構提供了兩個具體子類直接供我們使用:NSInvocationOperation和NSBlockOperation
2> 自訂子類繼承NSOperation,實現內部相應的方法

2.執行操作
NSOperation調用start方法即可開始執行操作,NSOperation對象預設按同步方式執行,也就是在調用start方法的那個線程中直接執行。NSOperation對象的isConcurrent方法會告訴我們這個操作相對於調用start方法的線程,是同步還是非同步執行。isConcurrent方法預設返回NO,表示操作與調用線程同步執行

3.取消操作
operation開始執行之後, 預設會一直執行操作直到完成,我們也可以調用cancel方法中途取消操作

複製代碼 代碼如下:

[operation cancel]; 

4.監聽操作的執行
如果我們想在一個NSOperation執行完畢後做一些事情,就調用NSOperation的setCompletionBlock方法來設定想做的事情
複製代碼 代碼如下:

operation.completionBlock = ^() { 
    NSLog(@"執行完畢"); 
}; 

或者
複製代碼 代碼如下:

[operation setCompletionBlock:^() { 
    NSLog(@"執行完畢"); 
}]; 

二、NSInvocationOperation
1.簡介
基於一個對象和selector來建立操作。如果你已經有現有的方法來執行需要的任務,就可以使用這個類

2.建立並執行操作

複製代碼 代碼如下:

// 這個操作是:調用self的run方法 
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; 
// 開始執行任務(同步執行) 
[operation start]; 

三、NSBlockOperation
1.簡介
能夠並發地執行一個或多個block對象,所有相關的block都執行完之後,操作才算完成

2.建立並執行操作

複製代碼 代碼如下:

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){ 
        NSLog(@"執行了一個新的操作,線程:%@", [NSThread currentThread]); 
}]; 
 // 開始執行任務(這裡還是同步執行) 
[operation start]; 

3.通過addExecutionBlock方法添加block操作
複製代碼 代碼如下:

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){ 
    NSLog(@"執行第1次操作,線程:%@", [NSThread currentThread]); 
}]; 
 
[operation addExecutionBlock:^() { 
    NSLog(@"又執行了1個新的操作,線程:%@", [NSThread currentThread]); 
}]; 
 
[operation addExecutionBlock:^() { 
    NSLog(@"又執行了1個新的操作,線程:%@", [NSThread currentThread]); 
}]; 
 
[operation addExecutionBlock:^() { 
    NSLog(@"又執行了1個新的操作,線程:%@", [NSThread currentThread]); 
}]; 
 
// 開始執行任務 
[operation start]; 

列印資訊如下:

2013-02-02 21:38:46.102 thread[4602:c07] 又執行了1個新的操作,線程:<NSThread: 0x7121d50>{name = (null), num = 1} 2013-02-02 21:38:46.102 thread[4602:3f03] 又執行了1個新的操作,線程:<NSThread: 0x742e1d0>{name = (null), num = 5} 2013-02-02 21:38:46.102 thread[4602:1b03] 執行第1次操作,線程:<NSThread: 0x742de50>{name = (null), num = 3} 2013-02-02 21:38:46.102 thread[4602:1303] 又執行了1個新的操作,線程:<NSThread: 0x7157bf0>{name = (null), num = 4} 

可以看出,這4個block是並發執行的,也就是在不同線程中執行的,num屬性可以看成是線程的id

四、自訂NSOperation
1.簡介
如果NSInvocationOperation和NSBlockOperation對象不能滿足需求, 你可以直接繼承NSOperation, 並添加任何你想要的行為。繼承所需的工作量主要取決於你要實現非並發還是並發的NSOperation。定義非並發的NSOperation要簡單許多,只需要重載-(void)main這個方法,在這個方法裡面執行主任務,並正確地響應取消事件; 對於並發NSOperation, 你必須重寫NSOperation的多個基本方法進行實現(這裡暫時先介紹非並發的NSOperation)

2.非並發的NSOperation
比如叫做DownloadOperation,用來下載圖片
1> 繼承NSOperation,重寫main方法,執行主任務
DownloadOperation.h

複製代碼 代碼如下:

#import <Foundation/Foundation.h> 
@protocol DownloadOperationDelegate; 
 
@interface DownloadOperation : NSOperation 
// 圖片的url路徑 
@property (nonatomic, copy) NSString *imageUrl; 
// 代理 
@property (nonatomic, retain) id<DownloadOperationDelegate> delegate; 
 
- (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate; 
@end 

複製代碼 代碼如下:
 
// 圖片下載的協議 
@protocol DownloadOperationDelegate <NSObject> 
- (void)downloadFinishWithImage:(UIImage *)image; 
@end 

DownloadOperation.m
複製代碼 代碼如下:

#import "DownloadOperation.h" 
 
@implementation DownloadOperation 
@synthesize delegate = _delegate; 
@synthesize imageUrl = _imageUrl; 
 
// 初始化 
- (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate { 
    if (self = [super init]) { 
        self.imageUrl = url; 
        self.delegate = delegate; 
    } 
    return self; 

// 釋放記憶體 
- (void)dealloc { 
    [super dealloc]; 
    [_delegate release]; 
    [_imageUrl release]; 

 
// 執行主任務 
- (void)main { 
    // 建立一個自動釋放池,如果是非同步執行操作,那麼將無法訪問到主線程的自動釋放池 
    @autoreleasepool { 
        // .... 
    } 

@end 

2> 正確響應取消事件
operation開始執行之後,會一直執行任務直到完成,或者顯式地取消操作。取消可能發生在任何時候,甚至在operation執行之前。儘管NSOperation提供了一個方法,讓應用取消一個操作,但是識別出取消事件則是我們自己的事情。如果operation直接終止, 可能無法回收所有已指派的記憶體或資源。因此operation對象需要檢測取消事件,並優雅地退出執行
NSOperation對象需要定期地調用isCancelled方法檢測操作是否已經被取消,如果返回YES(表示已取消),則立即退出執行。不管是自訂NSOperation子類,還是使用系統提供的兩個具體子類,都需要支援取消。isCancelled方法本身非常輕量,可以頻繁地調用而不產生大的效能損失
以下地方可能需要調用isCancelled:
* 在執行任何實際的工作之前
* 在迴圈的每次迭代過程中,如果每個迭代相對較長可能需要調用多次
* 代碼中相對比較容易中止操作的任何地方
DownloadOperation的main方法實現如下
複製代碼 代碼如下:

- (void)main { 
    // 建立一個自動釋放池,如果是非同步執行操作,那麼將無法訪問到主線程的自動釋放池 
    @autoreleasepool { 
        if (self.isCancelled) return; 
         
        // 擷取圖片資料 
        NSURL *url = [NSURL URLWithString:self.imageUrl]; 
        NSData *imageData = [NSData dataWithContentsOfURL:url]; 
         
        if (self.isCancelled) { 
            url = nil; 
            imageData = nil; 
            return; 
        } 
         
        // 初始化圖片 
        UIImage *image = [UIImage imageWithData:imageData]; 
         
        if (self.isCancelled) { 
            image = nil; 
            return; 
        } 
         
        if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) { 
            // 把圖片資料傳回到主線程 
            [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO]; 
        } 
    } 

NSOperationQueue
一、簡介
一個NSOperation對象可以通過調用start方法來執行任務,預設是同步執行的。也可以將NSOperation添加到一個NSOperationQueue(操作隊列)中去執行,而且是非同步執行的。
建立一個操作隊列:

複製代碼 代碼如下:

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 

二、添加NSOperation到NSOperationQueue中
1.添加一個operation
複製代碼 代碼如下:

[queue addOperation:operation]; 

2.添加一組operation
複製代碼 代碼如下:

[queue addOperations:operations waitUntilFinished:NO]; 

3.添加一個block形式的operation
複製代碼 代碼如下:

[queue addOperationWithBlock:^() { 
    NSLog(@"執行一個新的操作,線程:%@", [NSThread currentThread]); 
}]; 

NSOperation添加到queue之後,通常短時間內就會得到運行。但是如果存在依賴,或者整個queue被暫停等原因,也可能需要等待。
注意:NSOperation添加到queue之後,絕對不要再修改NSOperation對象的狀態。因為NSOperation對象可能會在任何時候運行,因此改變NSOperation對象的依賴或資料會產生不利的影響。你只能查看NSOperation對象的狀態, 比如是否正在運行、等待運行、已經完成等

三、添加NSOperation的依賴對象
1.當某個NSOperation對象依賴於其它NSOperation對象的完成時,就可以通過addDependency方法添加一個或者多個依賴的對象,只有所有依賴的對象都已經完成操作,當前NSOperation對象才會開始執行操作。另外,通過removeDependency方法來刪除依賴對象。

複製代碼 代碼如下:

[operation2 addDependency:operation1]; 

依賴關係不局限於相同queue中的NSOperation對象,NSOperation對象會管理自己的依賴, 因此完全可以在不同的queue之間的NSOperation對象建立依賴關係

唯一的限制是不能建立環形依賴,比如A依賴B,B依賴A,這是錯誤的

2.依賴關係會影響到NSOperation對象在queue中的執行順序,看下面的例子:
1> 沒有設定依賴關係

複製代碼 代碼如下:

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
 
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){ 
    NSLog(@"執行第1次操作,線程:%@", [NSThread currentThread]); 
}]; 
 
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){ 
    NSLog(@"執行第2次操作,線程:%@", [NSThread currentThread]); 
}]; 
 
[queue addOperation:operation1]; 
[queue addOperation:operation2]; 

列印資訊:

2013-02-03 00:21:35.024 thread[5616:3d13] 執行第1次操作,線程:<NSThread: 0x7658570>{name = (null), num = 3} 2013-02-03 00:21:35.063 thread[5616:1303] 執行第2次操作,線程:<NSThread: 0x765a2e0>{name = (null), num = 4} 

可以看出,預設是按照添加順序執行的,先執行operation1,再執行operation2

2> 設定了依賴關係

複製代碼 代碼如下:

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
 
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){ 
    NSLog(@"執行第1次操作,線程:%@", [NSThread currentThread]); 
}]; 
 
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){ 
    NSLog(@"執行第2次操作,線程:%@", [NSThread currentThread]); 
}]; 
// operation1依賴於operation2 
[operation1 addDependency:operation2]; 
 
[queue addOperation:operation1]; 
[queue addOperation:operation2]; 

列印資訊:

2013-02-03 00:24:16.260 thread[5656:1b03] 執行第2次操作,線程:<NSThread: 0x7634490>{name = (null), num = 3} 2013-02-03 00:24:16.285 thread[5656:1303] 執行第1次操作,線程:<NSThread: 0x9138b50>{name = (null), num = 4} 

可以看出,先執行operation2,再執行operation1

四、修改Operations的執行順序
對於添加到queue中的operations,它們的執行順序取決於2點:
1.首先看看NSOperation是否已經準備好:是否準備好由對象的依賴關係確定
2.然後再根據所有NSOperation的相對優先順序來確定。優先順序等級則是operation對象本身的一個屬性。預設所有operation都擁有“普通”優先順序,不過可以通過setQueuePriority:方法來提升或降低operation對象的優先順序。優先順序只能應用於相同queue中的operations。如果應用有多個operation queue,每個queue的優先順序等級是互相獨立的。因此不同queue中的低優先順序操作仍然可能比高優先順序操作更早執行。
注意:優先順序不能替代依賴關係,優先順序只是對已經準備好的 operations確定執行順序。先滿足依賴關係,然後再根據優先順序從所有準備好的操作中選擇優先順序最高的那個執行。

五、設定隊列的最大並行作業數量
隊列的最大並行作業數量,意思是隊列中最多同時運行幾條線程
雖然NSOperationQueue類設計用於並發執行Operations,你也可以強制單個queue一次只能執行一個Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大並行作業數量。設為1就表示queue每次只能執行一個操作。不過operation執行的順序仍然依賴於其它因素,比如operation是否準備好和operation的優先順序等。因此序列化的operation queue並不等同於GCD中的串列dispatch queue

複製代碼 代碼如下:

// 每次只能執行一個操作 
queue.maxConcurrentOperationCount = 1; 
// 或者這樣寫 
[queue setMaxConcurrentOperationCount:1]; 

六、取消Operations
一旦添加到operation queue,queue就擁有了這個Operation對象並且不能被刪除,唯一能做的事情是取消。你可以調用Operation對象的cancel方法取消單個操作,也可以調用operation queue的cancelAllOperations方法取消當前queue中的所有操作。
複製代碼 代碼如下:

// 取消單個操作 
[operation cancel]; 
 
// 取消queue中所有的操作 
[queue cancelAllOperations]; 

七、等待Options完成
為了最佳的效能,你應該設計你的應用儘可能地非同步作業,讓應用在Operation正在執行時可以去處理其它事情。如果需要在當前線程中處理operation完成後的結果,可以使用NSOperation的waitUntilFinished方法阻塞當前線程,等待operation完成。通常我們應該避免編寫這樣的代碼,阻塞當前線程可能是一種簡便的解決方案,但是它引入了更多的串列代碼,限制了整個應用的並發性,同時也降低了使用者體驗。絕對不要在應用主線程中等待一個Operation,只能在第二或次要線程中等待。阻塞主線程將導致應用無法響應使用者事件,應用也將表現為無響應。
複製代碼 代碼如下:

// 會阻塞當前線程,等到某個operation執行完畢 
[operation waitUntilFinished]; 

除了等待單個Operation完成,你也可以同時等待一個queue中的所有操作,使用NSOperationQueue的waitUntilAllOperationsAreFinished方法。注意:在等待一個 queue時,應用的其它線程仍然可以往queue中添加Operation,因此可能會加長線程的等待時間。
複製代碼 代碼如下:

// 阻塞當前線程,等待queue的所有操作執行完畢 
[queue waitUntilAllOperationsAreFinished]; 

八、暫停和繼續queue
如果你想臨時暫停Operations的執行,可以使用queue的setSuspended:方法暫停queue。不過暫停一個queue不會導致正在執行的operation在任務中途暫停,只是簡單地阻止調度新Operation執行。你可以在響應使用者請求時,暫停一個queue來暫停等待中的任務。稍後根據使用者的請求,可以再次調用setSuspended:方法繼續queue中operation的執行
複製代碼 代碼如下:

// 暫停queue 
[queue setSuspended:YES]; 
 
// 繼續queue 
[queue setSuspended:NO]; 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.