NSOperationQueue和GCD的队列类型
- NSOperationQueue
- 主队列[NSOperationQueue mainQueue]
凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行 - 非主队列(其他队列)
[[NSOperationQueue alloc] init].同时包含了:串行、并发功能添加到这种队列中的任务(NSOperation),就会自动放到子线程中执行
2.GCD
- 并发队列
全局、自己创建 - 串行队列
主队列、自己创建
NSOperation功能
- 开启任务
需要执行的操作封装到一个NSOperation对象中,然后将NSOperation对象添NSOperationQueue中系统会自动将NSOperationQueue中的NSOperation取出来,将取出的NSOperation封装的操作放到一条新线程中执行。NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类,系统提供的子类是
NSBlockOperation和NSInvocationOperation
//这里是在主线程执行的
self.blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"块内执行线程 %@",[NSThread currentThread]);
}];
//这里是子线程
[self.blockOperation addExecutionBlock:^{
NSLog(@"这是新加的块方法 %@",[NSThread currentThread]);
}];
[self.blockOperation start];
//主线程
NSInvocationOperation * op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(execute) object:nil];
[op start];
- 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
对于NSBlockOperation 只要block操作数大于1就会执行异步操作。
也可以自定义子类,方法非常简单只需要继承NSOperation后将操作写在main函数就可以。自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)。
经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应,对应操作比较耗时的时候。防止不能及时退出
NSBlockOperation 只要里面的操作块大于1就会进行异步操作。
设置依赖
NSOperation之间可以设置依赖来保证执行顺序,比如一定要让操作A执行完后,才能执行操作B,可以这么写
//设置op1依赖op2,不在一个队列也无所谓,不能相互依赖
NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任务1");
}];
NSInvocationOperation * op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
[op1 addDependency:op2];
NSOperationQueue * op1queue = [[NSOperationQueue alloc]init];
[op1queue addOperation:op1];
NSOperationQueue * op2queue = [[NSOperationQueue alloc]init];
[op2queue addOperation:op2];
NSOperationQueue
NSOperation可以调用start方法来执行任务,但默认是同步执行的如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作。
- 添加任务到队列
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
执行的任务顺序不是和添加的顺序一样的,没有必然关系。添加到队列里面后任务由队列调度运行,不用start了。
2.可以设置并发数目
并发数目设置为1的时候就是串行队列,大于1的时候就是并行队列
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- (void)cancelAllOperations;可以中断队列当前的所有任务
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
暂停和恢复队列
- (void)setSuspended:(BOOL)b;// YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
可以监听一个操作的执行完毕
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
下面是一个来自小码哥自己实现图片缓存的案例
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"app";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
XMGApp *app = self.apps[indexPath.row];
cell.textLabel.text = app.name;
cell.detailTextLabel.text = app.download;
// 先从内存缓存中取出图片
UIImage *image = self.images[app.icon];
if (image) { // 内存中有图片
cell.imageView.image = image;
} else { // 内存中没有图片
// 获得Library/Caches文件夹
NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 获得文件名
NSString *filename = [app.icon lastPathComponent];
// 计算出文件的全路径
NSString *file = [cachesPath stringByAppendingPathComponent:filename];
// 加载沙盒的文件数据
NSData *data = [NSData dataWithContentsOfFile:file];
if (data) { // 直接利用沙盒中图片
UIImage *image = [UIImage imageWithData:data];
cell.imageView.image = image;
// 存到字典中
self.images[app.icon] = image;
} else { // 下载图片
[self.queue addOperationWithBlock:^{
// 下载图片
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:app.icon]];
UIImage *image = [UIImage imageWithData:data];
[NSThread sleepForTimeInterval:1.0];
// 回到主线程显示图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.imageView.image = image;
}];
// 存到字典中
self.images[app.icon] = image;
// 将图片文件数据写入沙盒中
[data writeToFile:file atomically:YES];
}];
}
}
return cell;
}