本文源码 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联系我。 本文版权归做者和本网站共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接。