下面簡單介紹這三個方法
1.NSThread
調用方法如下:
如函數需要輸入參數,可從object傳進去。
( 1 ) [ N S T h r e a d d e t a c h N e w T h r e a d S e l e c t o r : @ s e l e c t o r ( t h r e a d In M a i n M e t h o d : ) t o T a r g e t : s e l f w i t h Object:nil];
(2) NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadInM ainMethod:) object:nil];
[myThread start];
(3) [obj performSelectorInBackground:@selector(threadMe) withObject:nil];
提個問題:如果某個ViewController裏運行了一個Thread,Thread還沒結束的時候,這個ViewC ontroller被Release了,結果會如何?
|
本文目錄
回到頂部
前言
NSOperation有三種狀態
NSOperationQueue只有當它管理的所有操作的isFinished屬性全標爲YES以後操作才停止出列,也就是隊列停止運行,所以正確實現這個方法對於避免死鎖很關鍵。 1.上一講簡單介紹了NSThread的使用,雖然也可以實現多線程編程,但是需要我們去管理線程的生命週期,還要考慮線程同步、加鎖問題,造成一些性能上的開銷。我們也可以配合使用NSOperation和NSOperationQueue實現多線程編程,實現步驟大致是這樣的:
1> 先將需要執行的操作封裝到一個NSOperation對象中 2> 然後將NSOperation對象添加到NSOperationQueue中 3> 系統會自動將NSOperation中封裝的操作放到一條新線程中執行 在此過程中,我們根本不用考慮線程的生命週期、同步、加鎖等問題 下面列舉一個應用場景,比如微博的粉絲列表: 每一行的頭像肯定要從新浪服務器下載圖片後才能顯示的,而且是需要異步下載。這時候你就可以把每一行的圖片下載操作封裝到一個NSOperation對象中,上面有6行,所以要創建6個NSOperation對象,然後添加到NSOperationQueue中,分別下載不同的圖片,下載完畢後,回到對應的行將圖片顯示出來。
2.默認情況下,NSOperation並不具備封裝操作的能力,必須使用它的子類,使用NSOperation子類的方式有3種:
1> NSInvocationOperation 2> NSBlockOperation 3> 自定義子類繼承NSOperation,實現內部相應的方法 這講先介紹如何用NSOperation封裝一個操作,後面再結合NSOperationQueue來使用。
回到頂部
一、NSInvocationOperation1 NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"mj"] autorelease];
2 [operation start];
* 第1行初始化了一個NSInvocationOperation對象,它是基於一個對象和selector來創建操作 * 第2行調用了start方法,緊接着會馬上執行封裝好的操作,也就是會調用self的run:方法,並且將@"mj"作爲方法參數 * 這裏要注意:默認情況下,調用了start方法後並不會開一條新線程去執行操作,而是在當前線程同步執行操作。只有將operation放到一個NSOperationQueue中,纔會異步執行操作。
回到頂部
二、NSBlockOperation1.同步執行一個操作1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
2 NSLog(@"執行了一個新的操作");
3 }];
4 // 開始執行任務
5 [operation start];
* 第1行初始化了一個NSBlockOperation對象,它是用一個Block來封裝需要執行的操作 * 第2行調用了start方法,緊接着會馬上執行Block中的內容 * 這裏還是在當前線程同步執行操作,並沒有異步執行
2.併發執行多個操作 1 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
2 NSLog(@"執行第1次操作,線程:%@", [NSThread currentThread]);
3 }];
4
5 [operation addExecutionBlock:^() {
6 NSLog(@"又執行了1個新的操作,線程:%@", [NSThread currentThread]);
7 }];
8
9 [operation addExecutionBlock:^() {
10 NSLog(@"又執行了1個新的操作,線程:%@", [NSThread currentThread]);
11 }];
12
13 [operation addExecutionBlock:^() {
14 NSLog(@"又執行了1個新的操作,線程:%@", [NSThread currentThread]);
15 }];
16
17 // 開始執行任務
18 [operation start];
* 第1行初始化了一個NSBlockOperation對象 * 分別在第5、9、13行通過addExecutionBlock:方法添加了新的操作,包括第1行的操作,一共封裝了4個操作 * 在第18行調用start方法後,就會併發地執行這4個操作,也就是會在不同線程中執行 1 2013-02-02 21:38:46.102 thread[4602:c07] 又執行了1個新的操作,線程:<NSThread: 0x7121d50>{name = (null), num = 1}
2 2013-02-02 21:38:46.102 thread[4602:3f03] 又執行了1個新的操作,線程:<NSThread: 0x742e1d0>{name = (null), num = 5}
3 2013-02-02 21:38:46.102 thread[4602:1b03] 執行第1次操作,線程:<NSThread: 0x742de50>{name = (null), num = 3}
4 2013-02-02 21:38:46.102 thread[4602:1303] 又執行了1個新的操作,線程:<NSThread: 0x7157bf0>{name = (null), num = 4}
可以看出,每個操作所在線程的num值都不一樣,說明是不同線程
回到頂部
三、NSOperation的其他用法1.取消操作operation開始執行之後, 默認會一直執行操作直到完成,我們也可以調用cancel方法中途取消操作 [operation cancel];
2.在操作完成後做一些事情如果想在一個NSOperation執行完畢後做一些事情,就調用NSOperation的setCompletionBlock方法來設置想做的事情 operation.completionBlock = ^() {
NSLog(@"執行完畢");
};
當operation封裝的操作執行完畢後,就會回調Block裏面的內容 NSOperation進階
優先級跟NSThread一樣,NSOpertion也可以設置優先級。
執行順序(依賴)有些時候想要控制執行順序,使用NSOpreation會方便多了,使用NSOpreation的Dependency就可以實現這種功能。
上面先執行第一個operation1,等operation1返回isFinish爲YES,即operation1完成了,纔會執行operation2。 注意死鎖:一定不可以循環依賴,像A依賴B,B依賴A,一定不要這樣做 CompletionBlock這個比較容易理解,就是每個NSOperation執行完畢之後,就會執行該block
執行結果
取消如前面所說,NSOperation有三種狀態,isReady -> isExecuting -> isFinish, 如果在Ready的狀態中對NSOperation進行取消,NSOperation會進入Finish狀態。但是Operation已經開始執行了,就會一直運行到結束,或者由我們進行顯示取消。也就是說Operation已經在executing狀態,我們調用cancle方法系統不會中止線程的,這需要我們在任務過程中檢測取消事件,並中止線程的執行,還要注意一點我們要釋放內存或資源。還是看一下實例代碼:
這種取消跟NSThread有點相似,調用cancle不會退出線程,需要你自已去中止線程,再exit;
回到頂部
四、自定義NSOperation如果NSInvocationOperation和NSBlockOperation不能滿足需求,我們可以直接新建子類繼承NSOperation,並添加任何需要執行的操作。如果只是簡單地自定義NSOperation,只需要重載-(void)main這個方法,在這個方法裏面添加需要執行的操作。
下面寫個子類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
// 圖片下載的協議
@protocol DownloadOperationDelegate <NSObject>
- (void)downloadFinishWithImage:(UIImage *)image;
@end
DownloadOperation.m 1 #import "DownloadOperation.h"
2
3 @implementation DownloadOperation
4 @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 // 釋放內存
16 - (void)dealloc {
17 [super dealloc];
18 [_imageUrl release];
19 }
20
21 // 執行主任務
22 - (void)main {
23 // 新建一個自動釋放池,如果是異步執行操作,那麼將無法訪問到主線程的自動釋放池
24 @autoreleasepool {
25 // ....
26 }
27 }
28 @end
* 在第22行重載了main方法,等會就把下載圖片的代碼寫到這個方法中 * 如果這個DownloadOperation是在異步線程中執行操作,也就是說main方法在異步線程調用,那麼將無法訪問主線程的自動釋放池,所以在第24行創建了一個屬於當前線程的自動釋放池
2.正確響應取消事件* 默認情況下,一個NSOperation開始執行之後,會一直執行任務到結束,就比如上面的DownloadOperation,默認會執行完main方法中的所有代碼。 * NSOperation提供了一個cancel方法,可以取消當前的操作。 * 如果是自定義NSOperation的話,需要手動處理這個取消事件。比如,一旦調用了cancel方法,應該馬上終止main方法的執行,並及時回收一些資源。 * 處理取消事件的具體做法是:在main方法中定期地調用isCancelled方法檢測操作是否已經被取消,也就是說是否調用了cancel方法,如果返回YES,表示已取消,則立即讓main方法返回。 * 以下地方可能需要調用isCancelled方法:
1 - (void)main {
2 // 新建一個自動釋放池,如果是異步執行操作,那麼將無法訪問到主線程的自動釋放池
3 @autoreleasepool {
4 if (self.isCancelled) return;
5
6 // 獲取圖片數據
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 // 把圖片數據傳回到主線程
26 [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO];
27 }
28 }
29 }
* 在第4行main方法的開頭就先判斷operation有沒有被取消。如果被取消了,那就沒有必要往下執行了 * 經過第8行下載圖片後,在第10行也需要判斷操作有沒有被取消 * 總之,執行了一段比較耗時的操作之後,都需要判斷操作有沒有被取消 * 圖片下載完畢後,在第26行將圖片數據傳遞給了代理(delegate)對象 支持併發
自定義併發的NSOperation就麻煩多了,需要實現以下方法,我們可以看一下下面這個表(來自蘋果官方):
3.GCD
見單獨一篇文章
|
|