多線(xiàn)程編程-NSOperation

多線(xiàn)程編程-NSOperation

本文目錄前言一、NSInvocationOperation

二、NSBlockOperation

三、NSOperation的其他用法四、自定義NSOperation回到頂部前言

1.上一講簡(jiǎn)單介紹了NSThread的使用,雖然也可以實(shí)現(xiàn)多線(xiàn)程編程,但是需要我們?nèi)ス芾砭€(xiàn)程的生命周期,還要考慮線(xiàn)程同步、加鎖問(wèn)題,造成一些性能上的開(kāi)銷(xiāo)。我們也可以配合使用NSOperation和NSOperationQueue實(shí)現(xiàn)多線(xiàn)程編程,實(shí)現(xiàn)步驟大致是這樣的:

1> 先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中

2> 然后將NSOperation對(duì)象添加到NSOperationQueue中

3> 系統(tǒng)會(huì)自動(dòng)將NSOperation中封裝的操作放到一條新線(xiàn)程中執(zhí)行在此過(guò)程中,我們根本不用考慮線(xiàn)程的生命周期、同步、加鎖等問(wèn)題下面列舉一個(gè)應(yīng)用場(chǎng)景,比如微博的粉絲列表:每一行的頭像肯定要從新浪服務(wù)器下載圖片后才能顯示的,而且是需要異步下載。

這時(shí)候你就可以把每一行的圖片下載操作封裝到一個(gè)NSOperation對(duì)象中,上面有6行,所以要?jiǎng)?chuàng)建6個(gè)NSOperation對(duì)象,然后添加到NSOperationQueue中,分別下載不同的圖片,下載完畢后,回到對(duì)應(yīng)的行將圖片顯示出來(lái)。?

2.默認(rèn)情況下,NSOperation并不具備封裝操作的能力,必須使用它的子類(lèi),使用NSOperation子類(lèi)的方式有3種:

1> NSInvocationOperation

2> NSBlockOperation

3> 自定義子類(lèi)繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法這講先介紹如何用NSOperation封裝一個(gè)操作,后面再結(jié)合NSOperationQueue來(lái)使用。

?回到頂部一、NSInvocationOperation

1 NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"mj"] autorelease];2 [operation start];

* 第1行初始化了一個(gè)NSInvocationOperation對(duì)象,它是基于一個(gè)對(duì)象和selector來(lái)創(chuàng)建操作

* 第2行調(diào)用了start方法,緊接著會(huì)馬上執(zhí)行封裝好的操作,也就是會(huì)調(diào)用self的run:方法,并且將@"mj"作為方法參數(shù)* 這里要注意:默認(rèn)情況下,調(diào)用了start方法后并不會(huì)開(kāi)一條新線(xiàn)程去執(zhí)行操作,而是在當(dāng)前線(xiàn)程同步執(zhí)行操作。

只有將operation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作。?

回到頂部二、NSBlockOperation

1.同步執(zhí)行一個(gè)操作1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){

2? ? ? ? NSLog(@"執(zhí)行了一個(gè)新的操作");

3 }];

4? // 開(kāi)始執(zhí)行任務(wù)

5 [operation start];

* 第1行初始化了一個(gè)NSBlockOperation對(duì)象,它是用一個(gè)Block來(lái)封裝需要執(zhí)行的操作

* 第2行調(diào)用了start方法,緊接著會(huì)馬上執(zhí)行Block中的內(nèi)容

* 這里還是在當(dāng)前線(xiàn)程同步執(zhí)行操作,并沒(méi)有異步執(zhí)行?

2.并發(fā)執(zhí)行多個(gè)操作復(fù)制代碼?

1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){?

2NSLog(@"執(zhí)行第1次操作,線(xiàn)程:%@", [NSThread currentThread]);?

3 }];?

5 [operation addExecutionBlock:^() {?

6NSLog(@"又執(zhí)行了1個(gè)新的操作,線(xiàn)程:%@", [NSThread currentThread]);

?7 }];?

?9 [operation addExecutionBlock:^() {

10 NSLog(@"又執(zhí)行了1個(gè)新的操作,線(xiàn)程:%@", [NSThread currentThread]);11 }];

13 [operation addExecutionBlock:^() {

14NSLog(@"又執(zhí)行了1個(gè)新的操作,線(xiàn)程:%@", [NSThread currentThread]);

15 }];

17 // 開(kāi)始執(zhí)行任務(wù)

18 [operation start];復(fù)制代碼* 第1行初始化了一個(gè)NSBlockOperation對(duì)象* 分別在第5、9、13行通過(guò)addExecutionBlock:方法添加了新的操作,包括第1行的操作,一共封裝了4個(gè)操作* 在第18行調(diào)用start方法后,就會(huì)并發(fā)地執(zhí)行這4個(gè)操作,也就是會(huì)在不同線(xiàn)程中執(zhí)行1?

2013-02-02 21:38:46.102 thread[4602:c07] 又執(zhí)行了1個(gè)新的操作,線(xiàn)程:{name = (null), num = 1}

2 2013-02-02 21:38:46.102 thread[4602:3f03] 又執(zhí)行了1個(gè)新的操作,線(xiàn)程:{name = (null), num = 5}

3 2013-02-02 21:38:46.102 thread[4602:1b03] 執(zhí)行第1次操作,線(xiàn)程:{name = (null), num = 3}

4 2013-02-02 21:38:46.102 thread[4602:1303] 又執(zhí)行了1個(gè)新的操作,線(xiàn)程:{name = (null), num = 4}可以看出,每個(gè)操作所在線(xiàn)程的num值都不一樣,說(shuō)明是不同線(xiàn)程 回到頂部三、NSOperation的其他用法

1.取消操作operation開(kāi)始執(zhí)行之后, 默認(rèn)會(huì)一直執(zhí)行操作直到完成,我們也可以調(diào)用cancel方法中途取消操作[operation cancel];?

2.在操作完成后做一些事情如果想在一個(gè)NSOperation執(zhí)行完畢后做一些事情,就調(diào)用NSOperation的setCompletionBlock方法來(lái)設(shè)置想做的事情operation.completionBlock = ^() {? ? NSLog(@"執(zhí)行完畢");};

當(dāng)operation封裝的操作執(zhí)行完畢后,就會(huì)回調(diào)Block里面的內(nèi)容 回到頂部四、自定義NSOperation如果NSInvocationOperation和NSBlockOperation不能滿(mǎn)足需求,我們可以直接新建子類(lèi)繼承NSOperation,并添加任何需要執(zhí)行的操作。如果只是簡(jiǎn)單地自定義NSOperation,只需要重載-(void)main這個(gè)方法,在這個(gè)方法里面添加需要執(zhí)行的操作。下面寫(xiě)個(gè)子類(lèi)DownloadOperation來(lái)下載圖片

1.繼承NSOperation,重寫(xiě)main方法DownloadOperation.h復(fù)制代碼

#import

@protocol DownloadOperationDelegate;

@interface DownloadOperation : NSOperation

// 圖片的url路徑

@property (nonatomic, copy) NSString *imageUrl;

// 代理@property (nonatomic, assign) iddelegate;

- (id)initWithUrl:(NSString *)url delegate:(id)delegate;@end

// 圖片下載的協(xié)議

@protocol DownloadOperationDelegate

- (void)downloadFinishWithImage:(UIImage *)image;

@end復(fù)制代碼DownloadOperation.m復(fù)制代碼?

1 #import "DownloadOperation.h"

?3 @implementation DownloadOperation?

4 @synthesize delegate = _delegate;?

5 @synthesize imageUrl = _imageUrl;?

6 // 初始化

8 - (id)initWithUrl:(NSString *)url delegate:(id)delegate {

9? ? if (self = [super init]) {

10? ? ? ? self.imageUrl = url;

11? ? ? ? self.delegate = delegate;

12? ? }

13? ? return self;

14 }

15 // 釋放內(nèi)存

16 - (void)dealloc {

17? ? [super dealloc];

18? ? [_imageUrl release];

19 }

21 // 執(zhí)行主任務(wù)

22 - (void)main {

23? ? // 新建一個(gè)自動(dòng)釋放池,如果是異步執(zhí)行操作,那么將無(wú)法訪(fǎng)問(wèn)到主線(xiàn)程的自動(dòng)釋放池

24? ? @autoreleasepool {

25? ? ? ? // ....

26? ? }

27 }

28 @end

復(fù)制代碼

* 在第22行重載了main方法,等會(huì)就把下載圖片的代碼寫(xiě)到這個(gè)方法中

* 如果這個(gè)DownloadOperation是在異步線(xiàn)程中執(zhí)行操作,也就是說(shuō)main方法在異步線(xiàn)程調(diào)用,那么將無(wú)法訪(fǎng)問(wèn)主線(xiàn)程的自動(dòng)釋放池,所以在第24行創(chuàng)建了一個(gè)屬于當(dāng)前線(xiàn)程的自動(dòng)釋放池

2.正確響應(yīng)取消事件

* 默認(rèn)情況下,一個(gè)NSOperation開(kāi)始執(zhí)行之后,會(huì)一直執(zhí)行任務(wù)到結(jié)束,就比如上面的DownloadOperation,默認(rèn)會(huì)執(zhí)行完main方法中的所有代碼。

* NSOperation提供了一個(gè)cancel方法,可以取消當(dāng)前的操作。

* 如果是自定義NSOperation的話(huà),需要手動(dòng)處理這個(gè)取消事件。比如,一旦調(diào)用了cancel方法,應(yīng)該馬上終止main方法的執(zhí)行,并及時(shí)回收一些資源。

* 處理取消事件的具體做法是:在main方法中定期地調(diào)用isCancelled方法檢測(cè)操作是否已經(jīng)被取消,也就是說(shuō)是否調(diào)用了cancel方法,如果返回YES,表示已取消,則立即讓main方法返回。

* 以下地方可能需要調(diào)用isCancelled方法:

在執(zhí)行任何實(shí)際的工作之前,也就是在main方法的開(kāi)頭。因?yàn)槿∠赡馨l(fā)生在任何時(shí)候,甚至在operation執(zhí)行之前。

執(zhí)行了一段耗時(shí)的操作之后也需要檢測(cè)操作是否已經(jīng)被取消

復(fù)制代碼

1 - (void)main {

2? ? // 新建一個(gè)自動(dòng)釋放池,如果是異步執(zhí)行操作,那么將無(wú)法訪(fǎng)問(wèn)到主線(xiàn)程的自動(dòng)釋放池

3? ? @autoreleasepool {

4? ? ? ? if (self.isCancelled) return;

5

6? ? ? ? // 獲取圖片數(shù)據(jù)

7? ? ? ? NSURL *url = [NSURL URLWithString:self.imageUrl];

8? ? ? ? NSData *imageData = [NSData dataWithContentsOfURL:url];

9

10? ? ? ? if (self.isCancelled) {

11? ? ? ? ? ? url = nil;

12? ? ? ? ? ? imageData = nil;

13? ? ? ? ? ? return;

14? ? ? ? }

15

16? ? ? ? // 初始化圖片

17? ? ? ? UIImage *image = [UIImage imageWithData:imageData];

18

19? ? ? ? if (self.isCancelled) {

20? ? ? ? ? ? image = nil;

21? ? ? ? ? ? return;

22? ? ? ? }

23

24? ? ? ? if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) {

25? ? ? ? ? ? // 把圖片數(shù)據(jù)傳回到主線(xiàn)程

26? ? ? ? ? ? [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO];

27? ? ? ? }

28? ? }

29 }

復(fù)制代碼

* 在第4行main方法的開(kāi)頭就先判斷operation有沒(méi)有被取消。如果被取消了,那就沒(méi)有必要往下執(zhí)行了

* 經(jīng)過(guò)第8行下載圖片后,在第10行也需要判斷操作有沒(méi)有被取消

* 總之,執(zhí)行了一段比較耗時(shí)的操作之后,都需要判斷操作有沒(méi)有被取消

* 圖片下載完畢后,在第26行將圖片數(shù)據(jù)傳遞給了代理(delegate)對(duì)象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容