一. NSThread 可以實(shí)現(xiàn)多線程編程,但是需要我們?nèi)ス芾砭€程的生命周期,還要考慮線程同步,加鎖問(wèn)題。造成一些性能上的開(kāi)銷(xiāo)。我們可以配合使用NSOperation 和NSOperationQueue 實(shí)現(xiàn)多線程編程。
使用步驟:
1.先將執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中
2.然后將NSOperation對(duì)象添加到NSOperationQueue中
3.系統(tǒng)會(huì)自動(dòng)將NSOperation封裝的操作放到一條新線程中執(zhí)行
在此過(guò)程中我們根本不需要考慮線程的生命周期,同步,加鎖等問(wèn)題。
比如以上微博粉絲列表,每一個(gè)頭像要從新浪的服務(wù)器下載后才能顯示,而且需要異步下載。這時(shí)候你就可以把每一行的圖片下載操作封裝到一個(gè)NSOperation對(duì)象中,上面有6行,所以要?jiǎng)?chuàng)建6個(gè)NSOperation對(duì)象,然后添加到NSOperationQueue中,分別下載不同的圖片,下載完畢后,回到對(duì)應(yīng)的行,將圖片顯示出來(lái)。
默認(rèn)情況下,NSOperation并不具備封裝操作的能力,必須使用它的子類(lèi),使用NSOperation子類(lèi)的方法有三種:
(1)NSInvocationOperation
(2)NSBlockOperation
(3)自定義子類(lèi)繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
使用方法:
(1)NSInvocationOperation
NSInvocationOperation *operation = [[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(runThread:) object:@"run"]autorelease]; //初始化一個(gè)NSinvocationOperation對(duì)象,它是基于一個(gè)對(duì)象和selector來(lái)創(chuàng)建的。
[operation start]; //調(diào)用start方法后并不會(huì)開(kāi)一條新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行操作.只有將operation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作.
(2)NSBlockOperation同步執(zhí)行一個(gè)操作
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^() {
NSLog(@"執(zhí)行一個(gè)新的操作");
}];
[operation start]; //開(kāi)始執(zhí)行任務(wù) 這里還是在當(dāng)前線程同步執(zhí)行操作,并沒(méi)有異步執(zhí)行
并發(fā)執(zhí)行多個(gè)操作
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^() {
NSLog(@"執(zhí)行第一次操作,線程:%@",[NSThread currentThread]);
}]; //初始化一個(gè)NSBlockOperation 對(duì)象
[operation addExecutionBlock:^(){
NSLog(@"又執(zhí)行了一個(gè)新的操作,線程:%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^(){
NSLog(@"又執(zhí)行了一個(gè)新的操作,線程:%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^(){
NSLog(@"又執(zhí)行了一個(gè)新的操作,線程:%@",[NSThread currentThread]);
}]; //通過(guò)addExecutionBlock:方法添加了新的操作,一共封裝了4個(gè)操作
[operation start]; //start之后,會(huì)在不同線程中,并發(fā)執(zhí)行這4個(gè)操作
[operation cancel]; //取消操作
操作完成之后的操作:如果想在一個(gè)NSOperation執(zhí)行完成之后做一些事情,就調(diào)用NSOperation的setCompletionBlock方法來(lái)設(shè)置想做的事情
operation.completionBlock = ^(){
NSLog(@"執(zhí)行完畢"); //當(dāng)operation封裝的操作執(zhí)行完畢后,就會(huì)回調(diào)執(zhí)行Block的內(nèi)容
};
(4)自定義NSOperation,重寫(xiě)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;
@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;
}
//釋放內(nèi)存
-(void)dealloc{
[super dealloc];
[_imageUrl release];
}
//執(zhí)行主任務(wù)
-(void)main{ //重載了main方法,等會(huì)就把下載的圖片寫(xiě)到這個(gè)方法中。
//新建一個(gè)自動(dòng)釋放池,如果是異步執(zhí)行操作,那么將無(wú)法訪問(wèn)到主線程的自動(dòng)釋放池
@autoreleasepool { //創(chuàng)建的是當(dāng)前線程的自動(dòng)釋放池
//...
}
}
@end
默認(rèn)一個(gè)NSOperation開(kāi)始執(zhí)行之后,會(huì)一直執(zhí)行到任務(wù)結(jié)束。
NSOperation 提供一個(gè)cancel方法,可以取消當(dāng)前的操作。
如果是自定義NSOperation的話,需要手動(dòng)處理這個(gè)取消事件。比如一旦調(diào)用了cancel方法,應(yīng)該馬上終止main方法的執(zhí)行,并及時(shí)回收一些資源。
處理取消事件的具體做法是:在main中定期的調(diào)用isCancelled方法監(jiān)測(cè)操作是否已經(jīng)被取消,也就是說(shuō)是否調(diào)用了cancel方法,如果返回YES,表示已取消,則立即讓main方法返回。
以下地方可能需要調(diào)用isCancelled方法:
(1)在執(zhí)行任何實(shí)際的工作之前,也就是說(shuō)在main方法的開(kāi)頭。因?yàn)槿∠赡馨l(fā)生在任何時(shí)候,甚至在operation執(zhí)行之前。
(2) 執(zhí)行了一段耗時(shí)的操作之后也需要監(jiān)測(cè)操作是否已經(jīng)被取消。
-(void)main{ //重載了main方法,等會(huì)就把下載的圖片寫(xiě)到這個(gè)方法中。
//新建一個(gè)自動(dòng)釋放池,如果是異步執(zhí)行操作,那么將無(wú)法訪問(wèn)到主線程的自動(dòng)釋放池
@autoreleasepool { //創(chuàng)建的是當(dāng)前線程的自動(dòng)釋放池
if (self.isCancelled) return; //判斷是否被取消,若取消了就沒(méi)有必要往下執(zhí)行了。
//獲取圖片數(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:)]) {
[(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO]; //將圖片數(shù)據(jù)傳給delegate對(duì)象
}
}
}
聯(lián)系客服