iOS开发基础37-多线程之NSOperation

在 iOS 开发中,除了 GCD (Grand Central Dispatch) 之外,NSOperationNSOperationQueue 也是进行多线程编程的强大工具。通过这套 API,可以更灵活地管理和控制并发任务。本文将详细介绍 NSOperationNSOperationQueue 的基本概念、具体使用方法、队列管理、其他高级用法及其底层逻辑。

一、基本概念

1. 简介

NSOperation 的作用

NSOperationNSOperationQueue 搭配使用可以实现多线程编程。其实现步骤如下:

  1. 将需要执行的操作封装到一个 NSOperation 对象中。
  2. NSOperation 对象添加到 NSOperationQueue 中。
  3. 系统会自动将队列中的 NSOperation 取出并放到新线程中执行。

2. NSOperation 的子类

NSOperation 是一个抽象类,并不具备直接封装操作的能力,必须使用其子类。

使用 NSOperation 子类的方式有三种:

  1. NSInvocationOperation
  2. NSBlockOperation
  3. 自定义子类继承 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

结语

NSOperationNSOperationQueue 提供了高层次的接口来管理并发操作,使得多线程编程更加简单、高效。通过合理地使用这些工具,开发者可以轻松实现并发任务的调度和执行,从而提升应用的性能和用户体验。

posted @ 2015-08-20 15:14  Mr.陳  阅读(547)  评论(0编辑  收藏  举报