[ios开发]nsoperation&nsoperationqueue(代码片段)

BillyMiracle BillyMiracle     2022-10-22     115

关键词:

NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。

(一)使用步骤

NSOperation实现多线程的使用步骤分为三步:

  1. 创建操作:先将需要执行的操作封装到一个NSOperation对象中
  2. 创建队列:创建NSOperationQueue对象
  3. 将操作加入到队列中:将NSOperation对象添加到NSOperationQueue对象中

之后,系统会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。

(二)基本使用

1. 创建操作

因为NSOperation是个抽象类,不能创建实例,所以我们通常使用它的子类来进行封装操作:

  • 使用子类NSInvocationOperation
  • 使用子类NSBlockOperation
  • 自定义继承自NSOperation的子类,通过实现内部相应的方法来封装操作

(1)使用子类NSInvocationOperation

// 1.创建 NSInvocationOperation 对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(testOp) object:nil];
// 2.调用 start 方法开始执行操作
[op start];

- (void)testOp 
    NSLog(@"testOp--%@", [NSThread currentThread]);

输出:

testOp--<_NSMainThread: 0x600003bb82c0>number = 1, name = main
  • 因为没有使用NSOperationQueue,并且我们是在当前线程(主线程)中执行一个操作,所以它是在当前线程(主线程)中完成操作的,并没有开启新线程。
  • 如果在其他线程中执行操作,则打印的结果为其他线程。总之,它是在哪个线程创建并启动的,它就会在哪个线程执行。

(2)使用子类NSBlockOperation

// 1.创建 NSBlockOperation 对象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"blockOp---%@", [NSThread currentThread]);
];
// 2.调用 start 方法开始执行操作
[op start];

输出:

blockOp---<_NSMainThread: 0x60000320c240>number = 1, name = main

这个其实和上边NSInvocationOperation一样,只是使用block之后更加简洁了,也没有开启新线程,哪个线程创建并执行就是那个线程。
NSBlockOperation还有一个方法addExecutionBlock:,通过addExecutionBlock:就可以为NSBlockOperation添加额外的操作。这些操作(包括blockOperationWithBlock中的操作)可以在不同的线程中同时(并发)执行。只有当所有相关的操作已经完成执行时,才视为完成。如果添加的操作多的话,blockOperationWithBlock:中的操作也有可能会在其他线程(非当前线程)中执行,这是由系统决定的,并不是说添加到blockOperationWithBlock:中的操作一定在当前线程中执行。

// 1.创建 NSBlockOperation 对象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"op---%@", [NSThread currentThread]);
];
// 2.添加额外操作
[op addExecutionBlock:^
    [NSThread sleepForTimeInterval:2];
    NSLog(@"1--Block task, %@", [NSThread currentThread]);
];
[op addExecutionBlock:^
    [NSThread sleepForTimeInterval:2];
    NSLog(@"2--Block task, %@", [NSThread currentThread]);
];
[op addExecutionBlock:^
    [NSThread sleepForTimeInterval:2];
    NSLog(@"3--Block task, %@", [NSThread currentThread]);
];
[op addExecutionBlock:^
    [NSThread sleepForTimeInterval:2];
    NSLog(@"4--Block task, %@", [NSThread currentThread]);
];
[op addExecutionBlock:^
    [NSThread sleepForTimeInterval:2];
    NSLog(@"5--Block task, %@", [NSThread currentThread]);
];
// 3.调用 start 方法开始执行操作
[op start];

输出:

op---<_NSMainThread: 0x600003fd0000>number = 1, name = main
3--Block task, <NSThread: 0x600003fd0200>number = 6, name = (null)
2--Block task, <NSThread: 0x600003fcb080>number = 7, name = (null)
4--Block task, <NSThread: 0x600003fcbb40>number = 5, name = (null)
1--Block task, <NSThread: 0x600003f890c0>number = 3, name = (null)
5--Block task, <NSThread: 0x600003f942c0>number = 4, name = (null)

使用子类NSBlockOperation,并调用addexecutionBlock:的情况下,blockOperationWithBlock:方法中的操作和额外加的操作是在不同线程中异步执行的。同时,额外操作多的时候,blockOperationWithBlock:方法中的操作有可能不会在当前线程中执行。
开启的线程数是由系统来决定的。
注意:addExecutionBlock: 方法必须在start()方法之前执行,否则就会报错。

(3)使用继承自NSOperation自定义子类

我们可以通过自定义继承自NSOperation的子类,重写main或者start来定义自己的NSOperation对象。

如果只是重写了main方法,有底层控制变更任务执行、完成状态以及任务退出。
如果重写了start方法,需要自己控制任务状态。
重写main方法比较简单,我们不需要管理线程的状态属性executing(是否正在执行)和finished(是否完成)。当main执行完返回的时候,这个操作就结束了。
重写main方法:

// .h 文件
#import <Foundation/Foundation.h>

@interface NSOperationTest : NSOperation
@end

// .m 文件
#import "NSOperationTest.h"

@implementation NSOperationTest
- (void)main 
    if (!self.isCancelled) 
        for (int i = 0; i < 2; i++) 
            [NSThread sleepForTimeInterval:2];
            NSLog(@"test---%@", [NSThread currentThread]);
        
    

@end

// 定义并执行
NSOperationTest *test = [[NSOperationTest alloc] init];
NSLog(@"%d", test.finished);
[test start];
NSLog(@"%d", test.finished);

输出:

0
test---<_NSMainThread: 0x600003508480>number = 1, name = main
test---<_NSMainThread: 0x600003508480>number = 1, name = main
1

重写start方法:

// .h 文件
#import <Foundation/Foundation.h>

@interface NSOperationTest : NSOperation
@end

// .m 文件
#import "NSOperationTest.h"

@implementation NSOperationTest
- (void)start 
    if (!self.isCancelled) 
        for (int i = 0; i < 2; i++) 
            [NSThread sleepForTimeInterval:2];
            NSLog(@"test---%@", [NSThread currentThread]);
        
    

@end

// 定义并执行
NSOperationTest *test = [[NSOperationTest alloc] init];
NSLog(@"%d", test.finished);
[test start];
NSLog(@"%d", test.finished);

输出:

0
test---<_NSMainThread: 0x60000386c000>number = 1, name = main
test---<_NSMainThread: 0x60000386c000>number = 1, name = main
0
  • 能看到,重写start方法系统没有自动管理线程的状态属性。
  • 没有使用NSOperationQueue,在主线程中创建自定义子类的操作对象并执行,并没有开启新线程。

2. 创建队列

NSOperationQueue共有两种队列:主队列、自定义队列,其中自定义队列同时包含了串行、并发功能。

  • 主队列:凡是添加到主队列中的操作,都会放到主线程中执行。
    // 主队列获取方法
    NSOperationQueue *queue = [NSOperationQueue mainQueue]; 
    
  • 自定义队列(非主队列):添加到这种队列中的操作,会自动放到子线程中执行,同时包含了 串行、并发 功能。
    // 自定义队列创建方法
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    

3. 将操作加入队列中

有两种方法:

(1)- (void)addOperation:(NSOperation *)op;

需要先创建操作,再将创建好的操作加入到创建好的队列中去。

/**
 * 使用 addOperation: 将操作加入到操作队列中
 */
- (void)addOperationToQueue 

    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2.创建操作
    // 使用 NSInvocationOperation 创建操作1
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];

    // 使用 NSInvocationOperation 创建操作2
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];

    // 使用 NSBlockOperation 创建操作3
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^
        for (int i = 0; i < 2; i++) 
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        
    ];
    [op3 addExecutionBlock:^
        for (int i = 0; i < 2; i++) 
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"4---%@", [NSThread currentThread]); // 打印当前线程
        
    ];

    // 3.使用 addOperation: 添加所有操作到队列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]

- (void)task1 
    for (int i = 0; i < 100; i++);
    NSLog(@"1---%@", [NSThread currentThread]);

- (void)task2 
    for (int i = 0; i < 100; i++);
    NSLog(@"2---%@", [NSThread currentThread]);

输出:

1---<NSThread: 0x6000002fa700>number = 7, name = (null)
2---<NSThread: 0x6000002a0480>number = 4, name = (null)
4---<NSThread: 0x6000002bd400>number = 8, name = (null)
3---<NSThread: 0x6000002e65c0>number = 5, name = (null)
3---<NSThread: 0x6000002e65c0>number = 5, name = (null)
4---<NSThread: 0x6000002bd400>number = 8, name = (null)

添加到队列中的操作开启了新线程来完成,进行并发执行。

(2)- (void)addOperationWithBlock:(void (^)(void))block;

无需先创建操作,在 block 中添加操作,直接将包含操作的 block 加入到队列中。

/**
 * 使用 addOperationWithBlock: 将操作加入到操作队列中
 */

- (void)addOperationWithBlockToQueue 
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // 2.使用 addOperationWithBlock: 添加操作到队列中
    [queue addOperationWithBlock:^
        for (int i = 0; i < 2; i++) 
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        
    ];
    [queue addOperationWithBlock:^
        for (int i = 0; i < 2; i++) 
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
        
    ];
    [queue addOperationWithBlock:^
        for (int i = 0; i < 2; i++) 
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"3---%@", [NSThread currentThread]); // 打印当前线程
        
    ];

输出:

3---<NSThread: 0x6000007c8e80>number = 8, name = (null)
1---<NSThread: 0x6000007901c0>number = 5, name = (null)
2---<NSThread: 0x600000790b80>number = 6, name = (null)
2---<NSThread: 0x600000790b80>number = 6, name = (null)
1---<NSThread: 0x6000007901c0>number = 5, name = (null)
3---<NSThread: 0x6000007c8e80>number = 8, name = (null)

可以看出,使用 addOperationWithBlock: 将操作加入到操作队列后能够开启新线程,进行并发执行。

(三)NSOperationQueue控制串行执行、并行执行

操作队列有一个属性,最大并发操作数,用来控制一个特定的队列中可以有多少个操作同时并发执行,也就是一个队列中同时能并发执行的最大操作数。

@property NSInteger maxConcurrentOperationCount;

注意:这里 maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行

  • maxConcurrentOperationCount默认为-1,表示不进行限制,可进行并发执行
  • maxConcurrentOperationCount为1时,此时队列为串行队列,只能串行执行
  • maxConcurrentOperationCount大于1时,队列为并发队列。操作并发执行。当这个值超过了系统限制,就会自动调整为系统设定的值。

对于开启线程数,是由系统决定的,不需要我们来管理。

(四)NSOperation操作依赖

NSOperationNSOperationQueue最吸引人的就是它能够添加操作之间的依赖关系,通过依赖关系,我们就可以很方便的控制操作之间的执行先后顺序。
NSOperation提供了3个接口供我们使用依赖:

  • - (void)addDependency:(NSOperation *)op添加依赖,是当前操作依赖op的完成,op完成之后才会执行当前操作。
  • - (void)removeDependency:(NSOperation *)op移除依赖,取消当前操作对操作op的依赖。
  • @property (readonly, copy) NSArray<NSOperation *> *dependencies;操作对象的一个属性,在当前操作开始执行之前完成执行的所有操作对象数组。

1. 没有添加依赖时

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
NSBlockOperation *firstOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"firstOperation");
];
NSBlockOperation *secondOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"secondOperation");
];
NSBlockOperation *thirdOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"thirdOperation");
];

[queue addOperation:firstOperation];
[queue addOperation:secondOperation];
[queue addOperation:thirdOperation];

输出:

firstOperation
thirdOperation
secondOperation

没有添加依赖,执行都是并发执行的,没有次序可言。

2. 添加依赖后

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

NSBlockOperation *firstOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"firstOperation");
];
NSBlockOperation *secondOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"secondOperation");
];
NSBlockOperation *thirdOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"thirdOperation");
];

[secondOperation addDependency:firstOperation]; // 让secondOperation依赖于firstOperation,即firstOperation先执行,在执行secondOperation
[thirdOperation addDependency:secondOperation]; // 让thirdOperation依赖于secondOperation,即secondOperation先执行,在执行thirdOperation

[queue addOperation:firstOperation];
[queue addOperation:secondOperation];
[queue addOperation:thirdOperation];

输出:

firstOperation
secondOperation
thirdOperation

在我们添加依赖之后,操作都是按照添加依赖的次序来的。

3. 相互依赖

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
NSBlockOperation *firstOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"firstOperation");
];
NSBlockOperation *secondOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"secondOperation");
];
NSBlockOperation *thirdOperation = [NSBlockOperation blockOperationWithBlock:^
    NSLog(@"thirdOperation");
];

[secondOperation addDependency:firstOperation]; // 让secondOperation依赖于firstOperation,即firstOperation先执行,再执行secondOperation
[firstOperation addDependency:secondOperation]; // 让firstOperation依赖于secondOperation,即secondOperation先执行,再执行firstOperation

[queue addOperation:firstOperation];
[queue addOperationios开发-第04篇-网络-01-nsoperation&网络基础

实现:Cell图片下载程序源代码下载地址:点击打开链接1、Cell图片下载程序1.1界面1.2基本思路1>Storyboard中添加导航控制器,根控制器为UITableViewController2>新建HMAppsViewController,关联3>导入app.plist,新建HMApp,提供类方法,利... 查看详情

ios开发多线程篇09—nsoperation简单介绍

iOS开发多线程篇—NSOperation简单介绍一、NSOperation简介1.简单说明NSOperation的作?:配合使用NSOperation和NSOperationQueue也能实现多线程编程NSOperation和NSOperationQueue实现多线程的具体步骤:(1)先将需要执行的操作封装到一个NSOperatio... 查看详情

ios开发多线程编程2-nsoperation

1.简介NSOperation实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或非并发的方式执行这个操作。NSOperation本身是抽象基类,因此必须使用它的子类,使用NSOperation子类的方式有2种:1>Foundation框架提供了两个具... 查看详情

ios多线程开发系列之nsoperation(代码片段)

上一篇我们简单的对iOS多线程开发系列(一)三种多线程办法进行对比性能和操作的复杂度,并认真介绍了NSThread的使用。我们借助上一次的例子完全可以采取NSOperation方法进行实现NSOperation不具备封装操作的能力,必须使用... 查看详情

ios开发多线程篇11—自定义nsoperation

     iOS开发多线程篇—自定义NSOperation一、实现一个简单的tableView显示效果实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式)1.新建一个项目,让控制器继承自UITableViewController。1//2... 查看详情

ios开发多线程篇10—nsoperation基本操作

       iOS开发多线程篇—NSOperation基本操作一、并发数(1)并发数:同时执?行的任务数.比如,同时开3个线程执行3个任务,并发数就是3(2)最大并发数:同一时间最多只能执行的任务的个数。(3)最?大... 查看详情

ios开发多线程一:了解-nsoperation的基本使用

 #import"ViewController.h"@interfaceViewController()@end@implementationViewController-(void)touchesBegan:(NSSet<UITouch*>*)toucheswithEvent:(UIEvent*)event{[selfblockOperation];}/***1:NSOpe 查看详情

ios开发多线程四:nsoperation多图下载综合案例

#import"ViewController.h"#import"XMGAPP.h"@interfaceViewController()/**tableView的数据源*/@property(nonatomic,strong)NSArray*apps;/**内存缓存*/@property(nonatomic,strong)NSMutableDictionary*images;/**队列*/@pro 查看详情

ios开发nsoperation三:操作依赖和监听以及线程间通信

一:操作依赖和监听#import"ViewController.h"@interfaceViewController()@end@implementationViewController/***1:NSOperation的使用:1:先创建队列NSOperationQueue:若不创建队列直接封装任务则默认在当前线程中串行执行任务,其队列分为两种主队列和非... 查看详情

为啥 NSOperation 示例代码使用@try & @catch

】为啥NSOperation示例代码使用@try&@catch【英文标题】:WhydoesNSOperationexamplecodeuses@try&@catch为什么NSOperation示例代码使用@try&@catch【发布时间】:2012-12-0216:06:47【问题描述】:在Apple的并发编程指南中,NSOperation子类示例(非... 查看详情

NSOperations 和 iOS 架构问题

】NSOperations和iOS架构问题【英文标题】:NSOperationsandiOSarchitecturequestions【发布时间】:2014-07-2502:49:02【问题描述】:我正在深入了解NSOperations的内容,并且我对iOS架构有疑问:*1.iOS系统架构中是否有可能杀死我调用的NSOperations/NS... 查看详情

ios多线程编程--nsoperation(代码片段)

...,我们需要实例化NSOperation的具体子类来进行多线程开发。3.GrandCentralDispatch:简称GCD,iOS4才开始支持.提供了一些新特性、运行库来支持多核并行编程,它的关注点更高:如何在多个cpu上提升效率了解GCD点这里... 查看详情

在 NSOperation 中查找 NSManagedObject

】在NSOperation中查找NSManagedObject【英文标题】:FindingNSManagedObjectinsideofNSOperation【发布时间】:2015-07-1021:37:44【问题描述】:鉴于objectID,我认为[mocexistingObjectWithID:self.objectIDerror:&amp;error]是在线程内查找现有对象的正确方法,... 查看详情

NS_ROOT_CLASS 对于 IOS 开发是不是“安全”?

】NS_ROOT_CLASS对于IOS开发是不是“安全”?【英文标题】:IsNS_ROOT_CLASS\'safe\'forIOSdevelopment?NS_ROOT_CLASS对于IOS开发是否“安全”?【发布时间】:2013-12-1215:19:19【问题描述】:我有一种情况,我有一个纯粹的静态无状态外观,用于... 查看详情

NSOperation(s) 仅在 iOS 3 设备上泄漏

】NSOperation(s)仅在iOS3设备上泄漏【英文标题】:NSOperation(s)leaksonlyoniOS3device【发布时间】:2010-09-3018:16:22【问题描述】:我有一些处理CoreData导入的NSOperations子类。我相信我已经勾选了大部分非主线程问题我在main方法我为每个人... 查看详情

ios多线程总结——nsoperation与nsoperationqueue的使用

...用,本编是线程实现总结的最后一篇,主要讲解NSOperation的使用。一.NSOperation在MacOSXv10.6和iOS4之前,NSOperation与NSOperationQueue不同于GCD,他们使用了完全不同的机制。从MacOSXv10.6和iOS4开始,NSOperation和NSOperationQueue... 查看详情

nsoperation的进阶使用和简单探讨(代码片段)

本文将会从这多个方面探讨NSOperation类和NSOperationQueue类的相关内容一、简介NSOperation的是iOS2.0推出的,通过NSThread实现的,但是效率的确一般。从OSX10.6和iOS4推出GCD时,又重写了NSOperation和NSOperationQueue,NSOperation和NSOperationQueue分别... 查看详情

ios多线程——你要知道的nsoperation都在这里(代码片段)

你要知道的iOS多线程NSThread、GCD、NSOperation、RunLoop都在这里转载请注明出处http://blog.csdn.net/u014205968/article/details/78323182本系列文章主要讲解iOS中多线程的使用,包括:NSThread、GCD、NSOperation以及RunLoop的使用方法详解,本系列... 查看详情