欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2016年02月14日 09:19:07   作者:M了個J  
這篇文章主要介紹了Objective-C的NSOperation多線程類基本使用指南,談到了Operations的執(zhí)行順序和并發(fā)量等設置操作,需要的朋友可以參考下

NSOperation

一、NSOperation
1.簡介
NSOperation實例封裝了需要執(zhí)行的操作和執(zhí)行操作所需的數(shù)據(jù),并且能夠以并發(fā)或非并發(fā)的方式執(zhí)行這個操作。
NSOperation本身是抽象基類,因此必須使用它的子類,使用NSOperation子類的方式有2種:
1> Foundation框架提供了兩個具體子類直接供我們使用:NSInvocationOperation和NSBlockOperation
2> 自定義子類繼承NSOperation,實現(xiàn)內部相應的方法

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

3.取消操作
operation開始執(zhí)行之后, 默認會一直執(zhí)行操作直到完成,我們也可以調用cancel方法中途取消操作

復制代碼 代碼如下:

[operation cancel]; 

4.監(jiān)聽操作的執(zhí)行
如果我們想在一個NSOperation執(zhí)行完畢后做一些事情,就調用NSOperation的setCompletionBlock方法來設置想做的事情
復制代碼 代碼如下:

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

或者
復制代碼 代碼如下:

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

二、NSInvocationOperation
1.簡介
基于一個對象和selector來創(chuàng)建操作。如果你已經(jīng)有現(xiàn)有的方法來執(zhí)行需要的任務,就可以使用這個類

2.創(chuàng)建并執(zhí)行操作

復制代碼 代碼如下:

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

三、NSBlockOperation
1.簡介
能夠并發(fā)地執(zhí)行一個或多個block對象,所有相關的block都執(zhí)行完之后,操作才算完成

2.創(chuàng)建并執(zhí)行操作

復制代碼 代碼如下:

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

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

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

打印信息如下:

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

可以看出,這4個block是并發(fā)執(zhí)行的,也就是在不同線程中執(zhí)行的,num屬性可以看成是線程的id

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

2.非并發(fā)的NSOperation
比如叫做DownloadOperation,用來下載圖片
1> 繼承NSOperation,重寫main方法,執(zhí)行主任務
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 

復制代碼 代碼如下:
 
// 圖片下載的協(xié)議 
@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]; 

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

@end 

2> 正確響應取消事件
operation開始執(zhí)行之后,會一直執(zhí)行任務直到完成,或者顯式地取消操作。取消可能發(fā)生在任何時候,甚至在operation執(zhí)行之前。盡管NSOperation提供了一個方法,讓應用取消一個操作,但是識別出取消事件則是我們自己的事情。如果operation直接終止, 可能無法回收所有已分配的內存或資源。因此operation對象需要檢測取消事件,并優(yōu)雅地退出執(zhí)行
NSOperation對象需要定期地調用isCancelled方法檢測操作是否已經(jīng)被取消,如果返回YES(表示已取消),則立即退出執(zhí)行。不管是自定義NSOperation子類,還是使用系統(tǒng)提供的兩個具體子類,都需要支持取消。isCancelled方法本身非常輕量,可以頻繁地調用而不產生大的性能損失
以下地方可能需要調用isCancelled:
* 在執(zhí)行任何實際的工作之前
* 在循環(huán)的每次迭代過程中,如果每個迭代相對較長可能需要調用多次
* 代碼中相對比較容易中止操作的任何地方
DownloadOperation的main方法實現(xiàn)如下
復制代碼 代碼如下:

- (void)main { 
    // 新建一個自動釋放池,如果是異步執(zhí)行操作,那么將無法訪問到主線程的自動釋放池 
    @autoreleasepool { 
        if (self.isCancelled) return; 
         
        // 獲取圖片數(shù)據(jù) 
        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:)]) { 
            // 把圖片數(shù)據(jù)傳回到主線程 
            [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO]; 
        } 
    } 

NSOperationQueue
一、簡介
一個NSOperation對象可以通過調用start方法來執(zhí)行任務,默認是同步執(zhí)行的。也可以將NSOperation添加到一個NSOperationQueue(操作隊列)中去執(zhí)行,而且是異步執(zhí)行的。
創(chuàng)建一個操作隊列:

復制代碼 代碼如下:

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(@"執(zhí)行一個新的操作,線程:%@", [NSThread currentThread]); 
}]; 

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

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

復制代碼 代碼如下:

[operation2 addDependency:operation1]; 

依賴關系不局限于相同queue中的NSOperation對象,NSOperation對象會管理自己的依賴, 因此完全可以在不同的queue之間的NSOperation對象創(chuàng)建依賴關系

201621491549333.png (460×222)

唯一的限制是不能創(chuàng)建環(huán)形依賴,比如A依賴B,B依賴A,這是錯誤的

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

復制代碼 代碼如下:

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

打印信息:

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

可以看出,默認是按照添加順序執(zhí)行的,先執(zhí)行operation1,再執(zhí)行operation2

2> 設置了依賴關系

復制代碼 代碼如下:

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

打印信息:

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

可以看出,先執(zhí)行operation2,再執(zhí)行operation1

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

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

復制代碼 代碼如下:

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

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

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

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

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

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

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

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

// 暫停queue 
[queue setSuspended:YES]; 
 
// 繼續(xù)queue 
[queue setSuspended:NO]; 

相關文章

最新評論