iOS开发基础37-多线程之NSOperation
在 iOS 开发中,除了 GCD (Grand Central Dispatch) 之外,NSOperation
和 NSOperationQueue
也是进行多线程编程的强大工具。通过这套 API,可以更灵活地管理和控制并发任务。本文将详细介绍 NSOperation
和 NSOperationQueue
的基本概念、具体使用方法、队列管理、其他高级用法及其底层逻辑。
一、基本概念
1. 简介
NSOperation 的作用
NSOperation
和 NSOperationQueue
搭配使用可以实现多线程编程。其实现步骤如下:
- 将需要执行的操作封装到一个
NSOperation
对象中。 - 将
NSOperation
对象添加到NSOperationQueue
中。 - 系统会自动将队列中的
NSOperation
取出并放到新线程中执行。
2. NSOperation 的子类
NSOperation
是一个抽象类,并不具备直接封装操作的能力,必须使用其子类。
使用 NSOperation
子类的方式有三种:
NSInvocationOperation
NSBlockOperation
- 自定义子类继承
NSOperation
实现相应的方法。
二、具体使用
1. 使用 NSInvocationOperation
创建 NSInvocationOperation
对象:
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
调用 start
方法开始执行操作:
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo) object:nil];
[op1 start];
注意:默认情况下,调用 start
方法不会开启新线程,而是在当前线程同步执行操作。只有将 NSOperation
添加到 NSOperationQueue
中,才会异步执行操作。
2. 使用 NSBlockOperation
创建 NSBlockOperation
对象:
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"Operation in thread: %@", [NSThread currentThread]);
}];
通过 addExecutionBlock:
方法添加更多操作:
[op1 addExecutionBlock:^{
NSLog(@"Additional operation 1 in thread: %@", [NSThread currentThread]);
}];
[op1 addExecutionBlock:^{
NSLog(@"Additional operation 2 in thread: %@", [NSThread currentThread]);
}];
调用 start
方法执行操作:
[op1 start];
注意:只要 NSBlockOperation
封装的操作数超过一个,就会异步执行操作。
3. 自定义子类继承 NSOperation
自定义子类通过重写 main
方法实现操作:
@implementation CustomOperation
- (void)main {
@autoreleasepool {
if (self.isCancelled) return;
// 你的操作代码
NSLog(@"Custom operation in thread: %@", [NSThread currentThread]);
if (self.isCancelled) return;
}
}
@end
然后创建并执行操作:
CustomOperation *op1 = [[CustomOperation alloc] init];
[op1 start];
三、NSOperationQueue
1. 简介
NSOperationQueue
的作用在于管理 NSOperation
对象并将其异步执行。添加操作到 NSOperationQueue
中:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:op1];
2. 最大并发数
并发数:同时执行的任务数(即同时启动的线程数)。
设置最大并发数的方法:
queue.maxConcurrentOperationCount = 3;
例如:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 3;
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];
NSLog(@"1 = %@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];
NSLog(@"2 = %@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1];
NSLog(@"3 = %@", [NSThread currentThread]);
}];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
3. 队列的取消、暂停、恢复
取消队列的所有操作
[queue cancelAllOperations];
暂停和恢复
- (void)setSuspended:(BOOL)b; // YES 表示暂停,NO 表示恢复
- (BOOL)isSuspended;
例如:
queue.suspended = YES; // 暂停
queue.suspended = NO; // 恢复
注意:取消和暂停操作后,当前正在执行的任务不会被取消或暂停,只会影响尚未执行的任务。
四、其他用法
1. 操作依赖
NSOperation
之间可以设置依赖关系。例如:
[op2 addDependency:op1]; // 操作 B 依赖于操作 A
注意:避免循环依赖,如 A 依赖 B,而 B 依赖 A。
2. 操作的监听
可以通过 completionBlock
监听操作的执行完毕:
op1.completionBlock = ^{
NSLog(@"Operation 1 completed");
};
3. 下载和合成图片示例
以下示例展示了如何异步下载两张图片并合成为一张:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
__block UIImage *image1 = nil;
__block UIImage *image2 = nil;
NSOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://example.com/image1.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image1 = [UIImage imageWithData:data];
}];
NSOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSURL *url = [NSURL URLWithString:@"http://example.com/image2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image2 = [UIImage imageWithData:data];
}];
NSOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
UIGraphicsBeginImageContext(CGSizeMake(200, 200));
[image1 drawInRect:CGRectMake(0, 0, 100, 200)];
[image2 drawInRect:CGRectMake(100, 0, 100, 200)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// 回到主线程更新 UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = newImage;
}];
}];
[op3 addDependency:op1];
[op3 addDependency:op2];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
}
4. 自定义 NSOperation
重写 main
方法:
@implementation CustomOperation
- (void)main {
@autoreleasepool {
if (self.isCancelled) return;
// 自定义操作代码
NSLog(@"Custom operation in thread: %@", [NSThread currentThread]);
if (self.isCancelled) return;
}
}
@end
结语
NSOperation
和 NSOperationQueue
提供了高层次的接口来管理并发操作,使得多线程编程更加简单、高效。通过合理地使用这些工具,开发者可以轻松实现并发任务的调度和执行,从而提升应用的性能和用户体验。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!