实现多线程的具体步骤
- 先将需要执行的操作封装到一个
NSOperation对象中 - 然后将
NSOperation对象添加到NSOperationQueue中 - 系统自动从
NSOperationQueue中取出NSOperation对象放到适合的线程中执行
NSOperation
- NSOperation是个
抽象类,并不具备封装操作的能力 - 只能通过它的
子类来使用它的功能 - 有三种方式来使用NSOperation
- NS
InvocationOperation - NS
BlockOperation -
自定义继承自NSOperation的操作
- NS
NSInvocationOperation
- 创建NS
InvocationOperation对象,通过对象方法创建
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
-
必须手动调用start方法才开始执行操作 -
默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程中同步执行操作
- (void)invocationOperation
{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 调用start方法
[op start];
}
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
NSBlockOperation
- 创建NS
BlockOperation对象,通过类方法创建
+ (id)blockOperationWithBlock:(void (^)(void))block;
- 通过
addExecutionBlock:方法添加多个操作
- (void)addExecutionBlock:(void (^)(void))block;
-
必须手动调用start方法才开始执行操作
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
{
// 这个任务是在【主线程】
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
[op start];
}
-
第一个任务是在当前线程中执行,后续的任务才会在子线程中执行 -
只有当封装的操作数> 1,才会异步执行操作
自定义继承自NSOperation的操作
- 重写
- (void)main方法,在里面实现想执行的任务逻辑 - 自己创建
自动释放池,如果操作是在子线程中完成,那么子线程中是无法访问主线程的自动释放池,所以自定Operation需要在外层添加@autoreleasepool{ } - 合理布局,在适当位置调用
isCancelled属性检测操作是否已被取消,及时中断操作
- (void)main
{
@autoreleasepool
{
for (NSInteger i = 0; i < 1000; i++)
{
NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
}
// 由于任务一旦开始执行就没办法停止下来
// 苹果官方建议,如果是自定义的Operation,而且内部的执行逻辑很耗时
// 如果外面调用了cancel方法,可以通过在一段耗时逻辑后调用一次isCancelled方法判断操作是否已经取消,以用来中断任务的执行
// 合理布局此方法
if (self.isCancelled) return;
for (NSInteger i = 0; i< 1000; i++)
{
NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i < 1000; i++)
{
NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
}
}
NSOperation设置依赖
- 比如任务A执行完后,才能执行任务B,可以这么设置
// 操作B依赖于操作A
[operationB addDependency:operationA];
-
可以在不同队列的NSOperation之间创建依赖关系
不同队列的NSOperation之间创建依赖关系
- NSOperation之间
不能相互依赖,比如:A依赖B,B依赖A
- (void)addDependencyTest
{
// 直接创建
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]);
}];
// 设置依赖后再添加到队列
// 当op1和op2都执行完才执行op3,但是op1和op2谁先执行完不确定
[op3 addDependency:op1];
[op3 addDependency:op2];
// 【不能】循环依赖
// [op3 addDependency:op1];
// [op1 addDependency:op3];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}
- NSOperation
执行完毕后想做一些收尾的事情,可以设置completionBlock属性
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
{
NSLog(@"download----%@", [NSThread currentThread]);
}];
// 任务执行完毕就会执行这个block
op.completionBlock = ^
{
// 也是在子线程中执行
NSLog(@"op执行完毕后执行---%@", [NSThread currentThread]);
};
NSOperationQueue
- 直接创建
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; - 如果将NSOperation添加到NSOperationQueue中,系统会
自动异步执行NSOperation中的操作 - 本质上NSOperationQueue内部也是自动调用了NSOperation的
start方法来执行任务 - 同时包含了
串行 和 并行功能
添加操作到NSOperationQueue中方法
- (void)addOperation:(NSOperation *)op;
// 推荐使用此种方式添加任务
- (void)addOperationWithBlock:(void (^)(void))block;
最大并发数
- 同时执行的最大任务数
- 当最大并发数为
1的时候就是串行队列,但所有任务不一定在同一个线程上执行完,可能在多个线程上执行,但一定是按顺序执行 - 最大并发数设置方式
// 调用示例
queue.maxConcurrentOperationCount = 1;
暂停和恢复队列:suspended
- 暂停后
还未执行的任务将不会被执行,但是已经开始的任务是不能立即停下来了
// 调用示例
queue.suspended = !self.queue.suspended;
- suspended的应用场景:队列正在执行一些耗时操作,当用户执行滚动操作时,为了用户体验可以调用这个属性先让队列中的任务暂停执行,以用来提高用户体验
取消队列的所有操作:cancelAllOperations
- 取消队列中的所有任务,会将队列中的任务全部移除
- 这个方法内部其实是调用了NSOperation中的
cancel方法 - 所以可以通过NSOperation中的cancel方法来取消
单个操作 -
只能取消还未执行的任务,已经开始的任务不能取消,除非任务中主动判断了是否取消操作queue.isCancelled来中断操作
// 调用示例
[queue cancelAllOperations];
线程间通信
- (void)test
{
[[[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;
}];
}];
}
GCD的队列和NSOperationQueue队列对比
GCD的队列
- 并发队列
- 自己创建的(
DISPATCH_QUEUE_CONCURRENT) - 全局队列
- 自己创建的(
- 串行队列
- 自己创建的(
DISPATCH_QUEUE_SERIAL or NULL) - 主队列(特殊的串行队列)
- 自己创建的(
NSOperationQueue队列类型
- 主队列
- [NSOperationQueue mainQueue]
- 凡是添加到主队列中的任务(NSOperation),都会放到
主线程中执行
- 非主队列(其他队列)
- 创建:[[NSOperationQueue alloc] init]
- 同时包含了
串行 和 并行功能 - 当设置最大并发数为
queue.maxConcurrentOperationCount = 1时就是串行队列 - 添加到队列中的操作,会
自动放到适合的线程中执行







网友评论