本文源碼 Demo 詳見 Github
https://github.com/shorfng/iOS-4.0-multithreading.gitgit
使用 NSOperation 的目的就是爲了讓開發人員再也不關心線程github
(1)先將須要執行的操做封裝到一個NSOperation對象中web
(2)而後將NSOperation對象添加到NSOperationQueue中編程
(3)系統會自動將NSOperationQueue中的NSOperation取出來緩存
(4)將取出的NSOperation封裝的操做放到一條新線程中執行網絡
NSOperation是個抽象類,並不具有封裝操做的能力,必須使用它的子類多線程
//建立NSInvocationOperation對象 - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg; //調用start方法開始執行操做,一旦執行操做,就會調用target的sel方法 - (void)start;
注意:併發
代碼示例:app
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //建立操做對象,封裝要執行的任務 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //執行操做 [op start]; } - (void)run { NSLog(@"------%@", [NSThread currentThread]); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
打印結果:框架
NSInvocationOperation[862:29437] ------<NSThread: 0x7f9cea507920>{number = 1, name = main}
//建立 NSBlockOperation 操做對象 + (id)blockOperationWithBlock:(void (^)(void))block; // 添加操做 - (void)addExecutionBlock:(void (^)(void))block;
注意:只要NSBlockOperation封裝的操做數 > 1,就會異步執行操做
代碼示例:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 1.建立 NSBlockOperation 操做對象 NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ // 在主線程 NSLog(@"下載1------%@", [NSThread currentThread]); }]; // 2.添加操做(額外的任務)(在子線程執行) [op addExecutionBlock:^{ NSLog(@"下載2------%@", [NSThread currentThread]); }]; [op addExecutionBlock:^{ [op addExecutionBlock:^{ NSLog(@"下載2------%@", [NSThread currentThread]); }]; [op addExecutionBlock:^{ NSLog(@"下載3------%@", [NSThread currentThread]); }]; [op addExecutionBlock:^{ NSLog(@"下載4------%@", [NSThread currentThread]); }]; // 3.開啓執行操做 [op start]; } - (void)run { NSLog(@"------%@", [NSThread currentThread]); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
打印結果:
NSBlockOperation[1013:37922] 下載1------<NSThread: 0x7feea1c05460>{number = 1, name = main} NSBlockOperation[1013:37952] 下載2------<NSThread: 0x7feea1f0b790>{number = 2, name = (null)} NSBlockOperation[1013:37955] 下載3------<NSThread: 0x7feea1c0f8a0>{number = 3, name = (null)} NSBlockOperation[1013:37951] 下載4------<NSThread: 0x7feea1e0b520>{number = 4, name = (null)}
NSOperationQueue的做用:添加操做到NSOperationQueue中,自動執行操做,自動開啓線程
添加操做到 NSOperationQueue 中:2種方式
- (void)addOperation:(NSOperation *)op; - (void)addOperationWithBlock:(void (^)(void))block NS_AVAILABLE(10_6, 4_0);
代碼示例:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self operationQueue2]; } #pragma mark - 把操做添加到隊列中,方式1:addOperation - (void)operationQueue1 { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.1 方式1:建立操做(任務)NSInvocationOperation ,封裝操做 NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil]; // 2.2 方式2:建立NSBlockOperation ,封裝操做 NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download2 --- %@", [NSThread currentThread]); }]; // 添加操做 [op2 addExecutionBlock:^{ NSLog(@"download3 --- %@", [NSThread currentThread]); }]; // 3.把操做(任務)添加到隊列中,並自動調用 start 方法 [queue addOperation:op1]; [queue addOperation:op2]; } - (void)download1 { NSLog(@"download1 --- %@", [NSThread currentThread]); } #pragma mark - 把操做添加到隊列中,方式2:addOperationWithBlock - (void)operationQueue2 { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.添加操做到隊列中 [queue addOperationWithBlock:^{ NSLog(@"download1 --- %@", [NSThread currentThread]); }]; [queue addOperationWithBlock:^{ NSLog(@"download2 --- %@", [NSThread currentThread]); }]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
打印結果:
NSOperationQueue[1658:89517] download2 --- <NSThread: 0x7f88a9e059d0>{number = 3, name = (null)} NSOperationQueue[1658:89518] download1 --- <NSThread: 0x7f88a9d901f0>{number = 2, name = (null)} NSOperationQueue[1658:89521] download3 --- <NSThread: 0x7f88a9d15d30>{number = 4, name = (null)} NSOperationQueue[1704:92509] download2 --- <NSThread: 0x7fd318f06540>{number = 2, name = (null)} NSOperationQueue[1704:92513] download1 --- <NSThread: 0x7fd318d0e460>{number = 3, name = (null)}
提示:隊列的取出是有順序的,與打印結果並不矛盾。這就比如,選手A,BC雖然起跑的順序是先A,後B,而後C,可是到達終點的順序卻不必定是A,B在前,C在後。
併發數:同時執⾏行的任務數 好比,同時開3個線程執行3個任務,併發數就是3
最大併發數:同一時間最多隻能執行的任務的個數
//最大併發數,默認爲-1 @property NSInteger maxConcurrentOperationCount; - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
說明:
代碼示例:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.設置最大併發操做數(大併發操做數 = 1,就變成了串行隊列) queue.maxConcurrentOperationCount = 2; // 3.添加操做 [queue addOperationWithBlock:^{ NSLog(@"download1 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:0.01]; }]; [queue addOperationWithBlock:^{ NSLog(@"download2 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:0.01]; }]; [queue addOperationWithBlock:^{ NSLog(@"download3 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:0.01]; }]; [queue addOperationWithBlock:^{ NSLog(@"download4 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:0.01]; }]; [queue addOperationWithBlock:^{ NSLog(@"download5 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:0.01]; }]; [queue addOperationWithBlock:^{ NSLog(@"download6 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:0.01]; }]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
打印結果:
最大併發數[1909:113433] download2 --- <NSThread: 0x7ffef240ba70>{number = 3, name = (null)} 最大併發數[1909:113432] download1 --- <NSThread: 0x7ffef24aee50>{number = 2, name = (null)} 最大併發數[1909:113432] download4 --- <NSThread: 0x7ffef24aee50>{number = 2, name = (null)} 最大併發數[1909:113431] download3 --- <NSThread: 0x7ffef251aa80>{number = 4, name = (null)} 最大併發數[1909:113428] download5 --- <NSThread: 0x7ffef2603d90>{number = 5, name = (null)} 最大併發數[1909:113432] download6 --- <NSThread: 0x7ffef24aee50>{number = 2, name = (null)}
隊列的暫停:當前任務結束後,暫停執行下一個任務,而非當前任務
//暫停和恢復隊列(YES表明暫停隊列,NO表明恢復隊列) - (void)setSuspended:(BOOL)b; //當前狀態 - (BOOL)isSuspended;
暫停和恢復的使用場合:
在tableview界面,開線程下載遠程的網絡界面,對UI會有影響,使用戶體驗變差。那麼這種狀況,就能夠設置在用戶操做UI(如滾動屏幕)的時候,暫停隊列(不是取消隊列),中止滾動的時候,恢復隊列。
代碼示例:
#import "ViewController.h" @interface ViewController () @property(nonatomic, strong) NSOperationQueue *queue; //隊列 @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.設置最大併發操做數(大併發操做數 = 1,就變成了串行隊列) queue.maxConcurrentOperationCount = 1; // 3.添加操做 [queue addOperationWithBlock:^{ NSLog(@"download1 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; [queue addOperationWithBlock:^{ NSLog(@"download2 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; [queue addOperationWithBlock:^{ NSLog(@"download3 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; [queue addOperationWithBlock:^{ NSLog(@"download4 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; self.queue = queue; } #pragma mark - 暫停和恢復 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (self.queue.isSuspended) { self.queue.suspended = NO; // 恢復隊列,繼續執行 } else { self.queue.suspended = YES; // 暫停(掛起)隊列,暫停執行 } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
打印結果:
隊列的暫停和恢復[2650:156206] download1 --- <NSThread: 0x7fd689f552b0>{number = 3, name = (null)} 隊列的暫停和恢復[2650:156205] download2 --- <NSThread: 0x7fd689c02e70>{number = 2, name = (null)} 隊列的暫停和恢復[2650:156206] download3 --- <NSThread: 0x7fd689f552b0>{number = 3, name = (null)} 隊列的暫停和恢復[2650:156385] download4 --- <NSThread: 0x7fd689ea11c0>{number = 4, name = (null)}
取消隊列的全部操做:相等於調用了全部 NSOperation 的 -(void)cancel 方法,
當前任務結束後,取消執行下面的全部任務,而非當前任務
// 也可調用NSOperation的 -(void)cancel 方法取消單個操做 - (void)cancelAllOperations;
代碼示例:
#import "ViewController.h" @interface ViewController () @property(nonatomic, strong) NSOperationQueue *queue; //隊列 @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.設置最大併發操做數(大併發操做數 = 1,就變成了串行隊列) queue.maxConcurrentOperationCount = 1; // 3.添加操做 [queue addOperationWithBlock:^{ NSLog(@"download1 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; [queue addOperationWithBlock:^{ NSLog(@"download2 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; [queue addOperationWithBlock:^{ NSLog(@"download3 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; [queue addOperationWithBlock:^{ NSLog(@"download4 --- %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:3]; }]; self.queue = queue; } #pragma mark - 取消隊列的全部操做 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 取消隊列的全部操做(相等於調用了全部NSOperation的-(void)cancel方法) [self.queue cancelAllOperations]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
打印結果:
隊列的取消[3041:167756] download1 --- <NSThread: 0x7fcc09543b40>{number = 3, name = (null)} 隊列的取消[3041:167749] download2 --- <NSThread: 0x7fcc094505f0>{number = 2, name = (null)}
設置NSOperation在queue中的優先級,能夠改變操做的執行優先級:
@property NSOperationQueuePriority queuePriority; - (void)setQueuePriority:(NSOperationQueuePriority)p;
優先級的取值:優先級高的任務,調用的概率會更大
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) { NSOperationQueuePriorityVeryLow = -8L, NSOperationQueuePriorityLow = -4L, NSOperationQueuePriorityNormal = 0, NSOperationQueuePriorityHigh = 4, NSOperationQueuePriorityVeryHigh = 8 };
NSOperation之間能夠設置依賴來保證執行順序:不能循環依賴(不能A依賴於B,B又依賴於A)
// 操做B依賴於操做A(必定要讓操做A執行完後,才能執行操做B) [operationB addDependency:operationA];
能夠在不一樣queue的NSOperation之間建立依賴關係(跨隊列依賴):
注意:
代碼示例:
#import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //建立對象,封裝操做 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download1----%@", [NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download2----%@", [NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download3----%@", [NSThread currentThread]); }]; NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{ for (NSInteger i = 0; i < 5; i++) { NSLog(@"download4----%@", [NSThread currentThread]); } }]; NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"download5----%@", [NSThread currentThread]); }]; //操做的監聽 op5.completionBlock = ^{ NSLog(@"op5執行完畢---%@", [NSThread currentThread]); }; //設置操做依賴(op4執行完,才執行 op3) [op3 addDependency:op1]; [op3 addDependency:op2]; [op3 addDependency:op4]; //把操做添加到隊列中 [queue addOperation:op1]; [queue addOperation:op2]; [queue addOperation:op3]; [queue addOperation:op4]; [queue addOperation:op5]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
打印結果:
操做依賴[4196:150518] download5----<NSThread: 0x7ffa61d177d0>{number = 3, name = (null)} 操做依賴[4196:150506] download1----<NSThread: 0x7ffa61ca6b90>{number = 4, name = (null)} 操做依賴[4196:150509] download4----<NSThread: 0x7ffa61f0e470>{number = 2, name = (null)} 操做依賴[4196:150510] download2----<NSThread: 0x7ffa61f0e800>{number = 5, name = (null)} 操做依賴[4196:150518] op5執行完畢---<NSThread: 0x7ffa61d177d0>{number = 3, name = (null)} 操做依賴[4196:150509] download4----<NSThread: 0x7ffa61f0e470>{number = 2, name = (null)} 操做依賴[4196:150509] download4----<NSThread: 0x7ffa61f0e470>{number = 2, name = (null)} 操做依賴[4196:150509] download4----<NSThread: 0x7ffa61f0e470>{number = 2, name = (null)} 操做依賴[4196:150509] download4----<NSThread: 0x7ffa61f0e470>{number = 2, name = (null)} 操做依賴[4196:150509] download3----<NSThread: 0x7ffa61f0e470>{number = 2, name = (null)}
能夠監聽一個操做的執行完畢:
@property (nullable, copy) void (^completionBlock)(void); - (void)setCompletionBlock:(void (^)(void))block;
代碼詳見4.5 操做依賴 示例代碼
#import "ViewController.h" @interface ViewController () @property(weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self test2]; } #pragma mark - 線程間通訊(圖片合成) - (void)test1 { // 1.隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; __block UIImage *image1 = nil; // 2.下載圖片1 NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{ // 圖片的網絡路徑 NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/" @"8/1/9981681/200910/11/1255259355826.jpg"]; // 加載圖片 NSData *data = [NSData dataWithContentsOfURL:url]; // 生成圖片 image1 = [UIImage imageWithData:data]; }]; __block UIImage *image2 = nil; // 3.下載圖片2 NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{ // 圖片的網絡路徑 NSURL *url = [NSURL URLWithString: @"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"]; // 加載圖片 NSData *data = [NSData dataWithContentsOfURL:url]; // 生成圖片 image2 = [UIImage imageWithData:data]; }]; // 4.合成圖片 NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{ // 開啓新的圖形上下文 UIGraphicsBeginImageContext(CGSizeMake(100, 100)); // 繪製圖片1 [image1 drawInRect:CGRectMake(0, 0, 50, 100)]; image1 = nil; // 繪製圖片2 [image2 drawInRect:CGRectMake(50, 0, 50, 100)]; image2 = nil; // 取得上下文中的圖片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // 結束上下文 UIGraphicsEndImageContext(); // 5.回到主線程 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; }]; }]; // 設置依賴操做 [combine addDependency:download1]; [combine addDependency:download2]; //把操做添加到隊列中 [queue addOperation:download1]; [queue addOperation:download2]; [queue addOperation:combine]; } #pragma mark - 線程間通訊(圖片下載) - (void)test2 { [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ // 圖片的網絡路徑 NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/" @"8/1/9981681/200910/11/1255259355826.jpg"]; // 加載圖片 NSData *data = [NSData dataWithContentsOfURL:url]; // 生成圖片 UIImage *image = [UIImage imageWithData:data]; // 回到主線程 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.imageView.image = image; }]; }]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
ViewController.m
#import "TDOperation.h" #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.建立自定義 TDGOperation TDOperation *op = [[TDOperation alloc] init]; // 3.把操做(任務)添加到隊列中,並自動調用 start 方法 [queue addOperation:op]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
TDOperation.h(繼承自:NSOperation)
#import <Foundation/Foundation.h> @interface TDOperation : NSOperation @end
TDOperation.m
#import "TDOperation.h" @implementation TDOperation //須要執行的任務 - (void)main { for (NSInteger i = 0; i < 3; i++) { NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]); } // 人爲的判斷是否執行取消操做,若是執行取消操做,就直接 return 不往下執行 if (self.isCancelled) return; for (NSInteger i = 0; i < 3; i++) { NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]); } // 人爲的判斷是否執行取消操做,若是執行取消操做,就直接 return 不往下執行 if (self.isCancelled) return; for (NSInteger i = 0; i < 3; i++) { NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]); } // 人爲的判斷是否執行取消操做,若是執行取消操做,就直接 return 不往下執行 if (self.isCancelled) return; } @end
運行結果:
自定義NSOperation[1567:84075] download1 -0-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download1 -1-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download1 -2-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download2 -0-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download2 -1-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download2 -2-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download3 -0-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download3 -1-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)} 自定義NSOperation[1567:84075] download3 -2-- <NSThread: 0x7fb6ba4109b0>{number = 2, name = (null)}
代碼示例:
ViewController.m #import "TDOperation.h" #import "ViewController.h" @interface ViewController () @property(nonatomic, strong) NSOperationQueue *queue; //隊列 @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.建立隊列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // 2.設置最大併發操做數(大併發操做數 = 1,就變成了串行隊列) queue.maxConcurrentOperationCount = 2; // 3.添加操做 - 自定義 NSOperation [queue addOperation:[[TDOperation alloc] init]]; self.queue = queue; } #pragma mark - 取消隊列的全部操做 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 取消隊列的全部操做(相等於調用了全部NSOperation的-(void)cancel方法) [self.queue cancelAllOperations]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
TDOperation.h
#import <Foundation/Foundation.h> @interface TDOperation : NSOperation @end
TDOperation.m
#import "TDOperation.h" @implementation TDOperation //須要執行的任務 - (void)main { for (NSInteger i = 0; i < 1000; i++) { NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]); } // 人爲的判斷是否執行取消操做,若是執行取消操做,就直接 return 不往下執行 if (self.isCancelled) return; for (NSInteger i = 0; i < 1000; i++) { NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]); } // 人爲的判斷是否執行取消操做,若是執行取消操做,就直接 return 不往下執行 if (self.isCancelled) return; for (NSInteger i = 0; i < 1000; i++) { NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]); } // 人爲的判斷是否執行取消操做,若是執行取消操做,就直接 return 不往下執行 if (self.isCancelled) return; } @end
Documents Library - Caches - Preference tmp
代碼示例:
ViewController.m
#import "TDApp.h" #import "ViewController.h" @interface ViewController () @property(nonatomic, strong) NSArray *apps; //全部數據 @property(nonatomic, strong) NSMutableDictionary *imageCache; //內存緩存的圖片 @property(nonatomic, strong) NSOperationQueue *queue; //隊列對象 @property(nonatomic, strong) NSMutableDictionary *operations; //全部的操做對象 @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } #pragma mark - 數據源方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.apps.count; } #pragma mark - Cell - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 重用標識 static NSString *ID = @"app"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; TDApp *app = self.apps[indexPath.row]; #pragma mark - app 名稱 cell.textLabel.text = app.name; #pragma mark - 下載量 cell.detailTextLabel.text = app.download; #pragma mark - 圖片 // 1.先從內存緩存中取出圖片 UIImage *image = self.imageCache[app.icon]; // 2.判斷內存中是否有圖片 if (image) { // 2.1 內存中有圖片,直接設置圖片 cell.imageView.image = image; } else { // 2.2 內存中沒有圖片,將圖片文件數據寫入沙盒中 //(1)得到Library/Caches文件夾 NSString *cachesPath = [NSSearchPathForDirectoriesInDomains( NSCachesDirectory, NSUserDomainMask, YES) firstObject]; //(2)得到文件名 NSString *filename = [app.icon lastPathComponent]; //(3)計算出文件的全路徑 NSString *file = [cachesPath stringByAppendingPathComponent:filename]; //(4)加載沙盒的文件數據 NSData *data = [NSData dataWithContentsOfFile:file]; // 2.3 判斷沙盒中是否有圖片 if (data) { // 有圖片,直接利用沙盒中圖片,設置圖片 UIImage *image = [UIImage imageWithData:data]; cell.imageView.image = image; // 並將圖片存到字典中 self.imageCache[app.icon] = image; } else { // 沒有圖片,先設置一個佔位圖 cell.imageView.image = [UIImage imageNamed:@"placeholder"]; // 取出圖片,並判斷這張圖片是否有下載操做 NSOperation *operation = self.operations[app.icon]; if (operation == nil) { // 若是這張圖片暫時沒有下載操做,則須要建立一個下載操做 // 下載圖片是耗時操做,放到子線程 operation = [NSBlockOperation blockOperationWithBlock:^{ // 下載圖片 NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]]; // 若是數據下載失敗 if (data == nil) { // 下載失敗,移除操做 [self.operations removeObjectForKey:app.icon]; return; } // 下載成功,將圖片放在 image 中 UIImage *image = [UIImage imageWithData:data]; // 存到字典中 self.imageCache[app.icon] = image; //回到主線程顯示圖片 [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [tableView reloadRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationNone]; }]; // 將圖片文件數據寫入沙盒中 [data writeToFile:file atomically:YES]; // 下載完畢,移除操做 [self.operations removeObjectForKey:app.icon]; }]; // 添加到隊列中(隊列的操做不須要移除,會自動移除) [self.queue addOperation:operation]; // 並將圖片存到字典中 self.operations[app.icon] = operation; } } } return cell; } #pragma mark - 數據懶加載 - (NSArray *)apps { if (!_apps) { NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]]; NSMutableArray *appArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { [appArray addObject:[TDApp appWithDict:dict]]; } _apps = appArray; } return _apps; } #pragma mark - 懶加載 - (NSMutableDictionary *)imageCache { if (!_imageCache) { _imageCache = [NSMutableDictionary dictionary]; } return _imageCache; } #pragma mark - 懶加載 - (NSOperationQueue *)queue { if (!_queue) { _queue = [[NSOperationQueue alloc] init]; _queue.maxConcurrentOperationCount = 3; } return _queue; } #pragma mark - 懶加載 - (NSMutableDictionary *)operations { if (!_operations) { _operations = [NSMutableDictionary dictionary]; } return _operations; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
TDApp.h
#import <Foundation/Foundation.h> @interface TDApp : NSObject @property(nonatomic, strong) NSString *icon; // 圖片 @property(nonatomic, strong) NSString *download; //下載量 @property(nonatomic, strong) NSString *name; // 名字 + (instancetype)appWithDict:(NSDictionary *)dict; @end
TDApp.m
#import "TDApp.h" @implementation TDApp + (instancetype)appWithDict:(NSDictionary *)dict { TDApp *app = [[self alloc] init]; [app setValuesForKeysWithDictionary:dict]; return app; } @end
SDWebImage:
框架地址:https://github.com/rs/SDWebImage
SDWebImage的圖片緩存週期是:1周
代碼示例:
ViewController.m
#import "TDApp.h" #import "UIImageView+WebCache.h" #import "ViewController.h" @interface ViewController () @property(nonatomic, strong) NSArray *apps; //全部數據 @property(nonatomic, strong) NSMutableDictionary *imageCache; //內存緩存的圖片 @property(nonatomic, strong) NSOperationQueue *queue; //隊列對象 @property(nonatomic, strong) NSMutableDictionary *operations; //全部的操做對象 @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } #pragma mark - 數據源方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.apps.count; } #pragma mark - Cell - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 重用標識 static NSString *ID = @"app"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; TDApp *app = self.apps[indexPath.row]; #pragma mark - app 名稱 cell.textLabel.text = app.name; #pragma mark - 下載量 cell.detailTextLabel.text = app.download; #pragma mark - 圖片 // expectedSize: 圖片的總字節數 receivedSize: 已經接收的圖片字節數 [cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.icon] placeholderImage:[UIImage imageNamed:@"placeholder"] options:0 // 0 表示什麼都不作 progress:^(NSInteger receivedSize, NSInteger expectedSize) { NSLog(@"下載進度:%f", 1.0 * receivedSize / expectedSize); } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { NSLog(@"下載完圖片"); }]; return cell; } #pragma mark - 數據懶加載 - (NSArray *)apps { if (!_apps) { NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"apps.plist" ofType:nil]]; NSMutableArray *appArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { [appArray addObject:[TDApp appWithDict:dict]]; } _apps = appArray; } return _apps; } #pragma mark - 懶加載 - (NSMutableDictionary *)imageCache { if (!_imageCache) { _imageCache = [NSMutableDictionary dictionary]; } return _imageCache; } #pragma mark - 懶加載 - (NSOperationQueue *)queue { if (!_queue) { _queue = [[NSOperationQueue alloc] init]; _queue.maxConcurrentOperationCount = 3; } return _queue; } #pragma mark - 懶加載 - (NSMutableDictionary *)operations { if (!_operations) { _operations = [NSMutableDictionary dictionary]; } return _operations; } #pragma mark - 設置控制器的內存警告 - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; self.imageCache = nil; self.operations = nil; [self.queue cancelAllOperations]; } @end
TDApp.h
#import <Foundation/Foundation.h> @interface TDApp : NSObject @property(nonatomic, strong) NSString *icon; // 圖片 @property(nonatomic, strong) NSString *download; //下載量 @property(nonatomic, strong) NSString *name; // 名字 + (instancetype)appWithDict:(NSDictionary *)dict; @end
TDApp.m
#import "TDApp.h" @implementation TDApp + (instancetype)appWithDict:(NSDictionary *)dict { TDApp *app = [[self alloc] init]; [app setValuesForKeysWithDictionary:dict]; return app; } @end
(1)併發隊列:手動建立、全局
(2)串行隊列:手動建立、主隊列
(1)主隊列:[NSOperationQueue mainQueue]
(2)其餘隊列(同時包含了串行、併發功能):[NSOperationQueue alloc]init]
注:關於SDWebImage框架的詳解會另外再寫博客
若是你以爲本篇文章對你有所幫助,請點擊文章末尾右下角「推薦」,^_^
做者:藍田(Loto)
出處:http://www.cnblogs.com/shorfng/
若有疑問,請在下方評論區回覆
OR發送郵件
至 shorfng@126.com聯繫我。 本文版權歸做者和本網站共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。