多线程编程2-NSOperation
- 前言
- 一、NSInvocationOperation
- 二、NSBlockOperation
- 三、NSOperation的其他用法
- 四、自定義NSOperation
前言
1.上一講簡單介紹了NSThread的使用,雖然也可以實現(xiàn)多線程編程,但是需要我們?nèi)ス芾砭€程的生命周期,還要考慮線程同步、加鎖問題,造成一些性能上的開銷。我們也可以配合使用NSOperation和NSOperationQueue實現(xiàn)多線程編程,實現(xiàn)步驟大致是這樣的:
1> 先將需要執(zhí)行的操作封裝到一個NSOperation對象中
2> 然后將NSOperation對象添加到NSOperationQueue中
3> 系統(tǒng)會自動將NSOperation中封裝的操作放到一條新線程中執(zhí)行
在此過程中,我們根本不用考慮線程的生命周期、同步、加鎖等問題
下面列舉一個應(yīng)用場景,比如微博的粉絲列表:
每一行的頭像肯定要從新浪服務(wù)器下載圖片后才能顯示的,而且是需要異步下載。這時候你就可以把每一行的圖片下載操作封裝到一個NSOperation對象中,上面有6行,所以要創(chuàng)建6個NSOperation對象,然后添加到NSOperationQueue中,分別下載不同的圖片,下載完畢后,回到對應(yīng)的行將圖片顯示出來。
?
2.默認(rèn)情況下,NSOperation并不具備封裝操作的能力,必須使用它的子類,使用NSOperation子類的方式有3種:
1>?NSInvocationOperation
2> NSBlockOperation
3> 自定義子類繼承NSOperation,實現(xiàn)內(nèi)部相應(yīng)的方法
這講先介紹如何用NSOperation封裝一個操作,后面再結(jié)合NSOperationQueue來使用。
?
回到頂部一、NSInvocationOperation
1 NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"mj"] autorelease]; 2 [operation start];* 第1行初始化了一個NSInvocationOperation對象,它是基于一個對象和selector來創(chuàng)建操作
* 第2行調(diào)用了start方法,緊接著會馬上執(zhí)行封裝好的操作,也就是會調(diào)用self的run:方法,并且將@"mj"作為方法參數(shù)
* 這里要注意:默認(rèn)情況下,調(diào)用了start方法后并不會開一條新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行操作。只有將operation放到一個NSOperationQueue中,才會異步執(zhí)行操作。
?
回到頂部二、NSBlockOperation
1.同步執(zhí)行一個操作
1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){ 2 NSLog(@"執(zhí)行了一個新的操作"); 3 }]; 4 // 開始執(zhí)行任務(wù) 5 [operation start];* 第1行初始化了一個NSBlockOperation對象,它是用一個Block來封裝需要執(zhí)行的操作
*?第2行調(diào)用了start方法,緊接著會馬上執(zhí)行Block中的內(nèi)容
* 這里還是在當(dāng)前線程同步執(zhí)行操作,并沒有異步執(zhí)行
?
2.并發(fā)執(zhí)行多個操作
1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){2 NSLog(@"執(zhí)行第1次操作,線程:%@", [NSThread currentThread]);3 }];4 5 [operation addExecutionBlock:^() {6 NSLog(@"又執(zhí)行了1個新的操作,線程:%@", [NSThread currentThread]);7 }];8 9 [operation addExecutionBlock:^() { 10 NSLog(@"又執(zhí)行了1個新的操作,線程:%@", [NSThread currentThread]); 11 }]; 12 13 [operation addExecutionBlock:^() { 14 NSLog(@"又執(zhí)行了1個新的操作,線程:%@", [NSThread currentThread]); 15 }]; 16 17 // 開始執(zhí)行任務(wù) 18 [operation start];* 第1行初始化了一個NSBlockOperation對象
* 分別在第5、9、13行通過addExecutionBlock:方法添加了新的操作,包括第1行的操作,一共封裝了4個操作
* 在第18行調(diào)用start方法后,就會并發(fā)地執(zhí)行這4個操作,也就是會在不同線程中執(zhí)行
1 2013-02-02 21:38:46.102 thread[4602:c07] 又執(zhí)行了1個新的操作,線程:<NSThread: 0x7121d50>{name = (null), num = 1} 2 2013-02-02 21:38:46.102 thread[4602:3f03] 又執(zhí)行了1個新的操作,線程:<NSThread: 0x742e1d0>{name = (null), num = 5} 3 2013-02-02 21:38:46.102 thread[4602:1b03] 執(zhí)行第1次操作,線程:<NSThread: 0x742de50>{name = (null), num = 3} 4 2013-02-02 21:38:46.102 thread[4602:1303] 又執(zhí)行了1個新的操作,線程:<NSThread: 0x7157bf0>{name = (null), num = 4}可以看出,每個操作所在線程的num值都不一樣,說明是不同線程
?
回到頂部三、NSOperation的其他用法
1.取消操作
operation開始執(zhí)行之后, 默認(rèn)會一直執(zhí)行操作直到完成,我們也可以調(diào)用cancel方法中途取消操作
[operation cancel];?
2.在操作完成后做一些事情
如果想在一個NSOperation執(zhí)行完畢后做一些事情,就調(diào)用NSOperation的setCompletionBlock方法來設(shè)置想做的事情
operation.completionBlock = ^() {NSLog(@"執(zhí)行完畢"); };當(dāng)operation封裝的操作執(zhí)行完畢后,就會回調(diào)Block里面的內(nèi)容
?
回到頂部四、自定義NSOperation
如果NSInvocationOperation和NSBlockOperation不能滿足需求,我們可以直接新建子類繼承NSOperation,并添加任何需要執(zhí)行的操作。如果只是簡單地自定義NSOperation,只需要重載-(void)main這個方法,在這個方法里面添加需要執(zhí)行的操作。
下面寫個子類DownloadOperation來下載圖片
1.繼承NSOperation,重寫main方法
DownloadOperation.h
#import <Foundation/Foundation.h> @protocol DownloadOperationDelegate;@interface DownloadOperation : NSOperation // 圖片的url路徑 @property (nonatomic, copy) NSString *imageUrl; // 代理 @property (nonatomic, assign) id<DownloadOperationDelegate> delegate;- (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate; @end// 圖片下載的協(xié)議 @protocol DownloadOperationDelegate <NSObject> - (void)downloadFinishWithImage:(UIImage *)image; @endDownloadOperation.m
1 #import "DownloadOperation.h"2 3 @implementation DownloadOperation4 @synthesize delegate = _delegate;5 @synthesize imageUrl = _imageUrl;6 7 // 初始化8 - (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)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 } 20 21 // 執(zhí)行主任務(wù) 22 - (void)main { 23 // 新建一個自動釋放池,如果是異步執(zhí)行操作,那么將無法訪問到主線程的自動釋放池 24 @autoreleasepool { 25 // .... 26 } 27 } 28 @end* 在第22行重載了main方法,等會就把下載圖片的代碼寫到這個方法中
* 如果這個DownloadOperation是在異步線程中執(zhí)行操作,也就是說main方法在異步線程調(diào)用,那么將無法訪問主線程的自動釋放池,所以在第24行創(chuàng)建了一個屬于當(dāng)前線程的自動釋放池
?
2.正確響應(yīng)取消事件
* 默認(rèn)情況下,一個NSOperation開始執(zhí)行之后,會一直執(zhí)行任務(wù)到結(jié)束,就比如上面的DownloadOperation,默認(rèn)會執(zhí)行完main方法中的所有代碼。
* NSOperation提供了一個cancel方法,可以取消當(dāng)前的操作。
* 如果是自定義NSOperation的話,需要手動處理這個取消事件。比如,一旦調(diào)用了cancel方法,應(yīng)該馬上終止main方法的執(zhí)行,并及時回收一些資源。
* 處理取消事件的具體做法是:在main方法中定期地調(diào)用isCancelled方法檢測操作是否已經(jīng)被取消,也就是說是否調(diào)用了cancel方法,如果返回YES,表示已取消,則立即讓main方法返回。
* 以下地方可能需要調(diào)用isCancelled方法:
- 在執(zhí)行任何實際的工作之前,也就是在main方法的開頭。因為取消可能發(fā)生在任何時候,甚至在operation執(zhí)行之前。
- 執(zhí)行了一段耗時的操作之后也需要檢測操作是否已經(jīng)被取消
* 在第4行main方法的開頭就先判斷operation有沒有被取消。如果被取消了,那就沒有必要往下執(zhí)行了
* 經(jīng)過第8行下載圖片后,在第10行也需要判斷操作有沒有被取消
* 總之,執(zhí)行了一段比較耗時的操作之后,都需要判斷操作有沒有被取消
* 圖片下載完畢后,在第26行將圖片數(shù)據(jù)傳遞給了代理(delegate)對象
?
轉(zhuǎn)載于:https://www.cnblogs.com/pb89/p/4708822.html
總結(jié)
以上是生活随笔為你收集整理的多线程编程2-NSOperation的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Build.VERSIO
- 下一篇: 工行生肖信用卡有哪些权益?五大权益服务到