继之前的NSThread、GCD,今天学习下NSOperation。NSOperation是个抽象类,并不直接实现多线程编程的能力,必须使用它的子类,配合使用NSOperationQueue队列实现多线程编程。
- NSInvocationOperation
- (通过initWithTarget:执行任务) - NSBlockOperation
- (通过block执行任务) - 自定义子类继承自NSOperation,实现内部相应的方法
- (通过重写父类的方法,执行任务)
NSInvocationOperation 的运用
// 通过 NSInvocationOperation 创建任务
-(void)NSInvocationOperation {
// 创建任务
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(do_method) object:nil];
// 开始任务
[operation start];
}
-(void)do_method {
NSLog(@"---任务01---%@",[NSThread currentThread]);
}
总结:默认主线程执行,不开启新线程
NSBlockOperation 的运用
// 通过 NSBlockOperation 创建任务
-(void)NSBlockOperation {
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---任务01---%@",[NSThread currentThread]);
}];
// 只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
// 追加额外的任务
[operation addExecutionBlock:^{
NSLog(@"---任务02---%@",[NSThread currentThread]);
}];
// 追加额外的任务
[operation addExecutionBlock:^{
NSLog(@"---任务03---%@",[NSThread currentThread]);
}];
// 开始队列任务
[operation start];
}
总结:如果任务只有一条,不开启新线程,主线程执行;当添加额外的任务时,会开启新的线程去执行任务
自定义 NSOperation 的运用
需要在自定义的XMOperation文件中重写 - (void)main; 方法
// 将需要执行的任务方法main函数中,当外界调用自己实例的start方法时,执行该任务
-(void)main {
NSLog(@"---我是自定义队列任务---%@",[NSThread currentThread]);
}
// 自定义 NSOperation
-(void)DIYOperation {
// 自定义继承自 NSOperation 的任务
XMOperation *operation = [[XMOperation alloc] init];
// 开始任务(去实现自定义类方法中的main函数)
[operation start];
}
总结:当调用 [operation start] 后执行 - (void)main; 方法
NSOperationQueue 的运用
-(void)operationQueue {
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 通过 NSInvocationOperation 创建任务
NSInvocationOperation *opt = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(do_method) object:nil];
// 通过 NSBlockOperation 创建任务
NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---任务02---%@",[NSThread currentThread]);
}];
// 添加任务到队列中
[queue addOperation:operation];
[queue addOperation:opt];
// 直接向队列中添加任务
[queue addOperationWithBlock:^{
NSLog(@"---任务03---%@",[NSThread currentThread]);
}];
}
总结:开启多条线程,并且并发执行任务
最大并发数的使用(maxConcurrentOperationCount)
-(void)maxConcurrentOperationCount {
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 确定最大并发数,当并发数为1时,是串行队列
// 当并发数为n(n>1)时,是并发队列,每次并发执行n条任务
queue.maxConcurrentOperationCount = 2;
// 添加任务到队列中
[queue addOperationWithBlock:^{
NSLog(@"---任务01---%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"---任务02---%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"---任务03---%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"---任务04---%@",[NSThread currentThread]);
}];
}
- 图片 01 (最大并发数为2)
- 图片 02 (最大并发数为1)
总结:当最大并发数为1时,开启新线程,但一般只会开启一条,因为此时是串行执行。见图片 01
当最大并发数大于1时(n(n>1)),开启多条线程,并发执行n条任务。见图片 01
对任务添加依赖的运用(addDependency)
// 添加依赖
-(void)addDependency {
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 通过 NSBlockOperation 创建任务
NSBlockOperation *opt1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---任务01---%@",[NSThread currentThread]);
}];
NSBlockOperation *opt2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---任务02---%@",[NSThread currentThread]);
}];
NSBlockOperation *opt3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---任务03---%@",[NSThread currentThread]);
}];
NSBlockOperation *opt4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"---任务04---%@",[NSThread currentThread]);
}];
// 添加依赖
[opt3 addDependency:opt4]; // 线程opt3 需要在 opt4 执行完毕后执行
[opt2 addDependency:opt3]; // 线程opt2 需要在 opt3 执行完毕后执行
[opt1 addDependency:opt2]; // 线程opt1 需要在 opt2 执行完毕后执行
// 添加任务到队列中
[queue addOperation:opt1];
[queue addOperation:opt2];
[queue addOperation:opt3];
[queue addOperation:opt4];
}
总结:开启新线程,但执行顺序受依赖的影响
为每条任务添加依赖,也可以实现串行队列
队列的状态
- 暂停(suspended = YES)
- 执行(suspended = NO)
- 取消所有任务 (cancelAllOperations)
- 判断是否暂停 (isSuspended)
if (self.queue.isSuspended) { // 队列处于暂停状态
// 恢复队列,继续执行
self.queue.suspended = NO;
} else { // 队列处于正常状态
// 暂停(挂起)队列,暂停执行
self.queue.suspended = YES;
}
// 取消所有队列
[self.queue cancelAllOperations];
线程之间的通信
线程之间的通信模板
// 线程之间的通信
-(void)communicateWithOperation {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"---子线程任务---%@",[NSThread currentThread]);
// 回到主线程执行任务
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"---主线程任务---%@",[NSThread currentThread]);
}];
}];
}
利用NSOperationQueue在子线程下载图片,主线程显示图片
// 线程之间的通信实例
-(void)communicateWithOperationInstance {
// 开辟一个子线程执行图片下载任务
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://xxx.png"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程,添加图片
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}
总结:多线程的学习就到这里了,NSThread、GCD、NSOperation 这三条多线程方法在iOS开发中运用还是比较频繁的,其中GCD的功能最多,可以实现单例,延迟执行,多线程遍历,线程群组,线程阻碍等等。对于任务的分段下载,既可以用GCD的群组,也可以用NSOperation的依赖来实现。最后说一句,学习,一定得去敲,得去写,进步才会快