IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> [iOS开发]NSOperation、NSOperationQueue -> 正文阅读

[移动开发][iOS开发]NSOperation、NSOperationQueue

简介概念了解

在SDWebImage中有见过NSOperation这个函数

关于NSOperation我们可以看到官方文档中的解释:
表示于单个任务相关联的代码和数据的抽象类
因为其是一个抽象类,我们不能直接使用它,而是使用它的子类或使用系统定义的子类(NSInvocationOperation或NSBlockOperation)来执行实际的任务。尽管是抽象的,NSOperation的基本实现确实包含了重要的逻辑来协调任务的安全执行。这种内置逻辑的存在允许您将注意力集中在任务的实际实现上,而不是确保它与其他系统对象正确公所所需的粘合代码上。

通常通过将操作添加到操作队列(NSOperationQueue类的实例)来执行操作。

如果不想使用操作队列,可以直接从代码中调用操作的start方法来执行操作。手动执行操作会给代码带来更大的负担,因为启动未处于就绪状态的操作会触发异常。

操作队列是OC中的一种高级的并发处理方法,现在它是基于GCD更高一层的封装,完全面向对象。但是比GCD更加简单易用、代码可读性也更高。

操作队列不同于GCD中FIFO的原则。NSOperationQueue对于添加到队列中的操作,首先进入准备就绪的状态(就绪状态取决于操作之间的依赖关系),然后进入就绪状态的操作开始执行顺序由操作之间的优先级决定。

操作队列也为我们提供了两种不同类型的队列:主队列和自定队列,主队列运行在主线程之上,而自定义队列在后台执行

为什么我们需要使用NSOperation、NSOperationQueue

  • 可以添加完成的代码块,在操作完成之后执行
  • 添加操作之间的依赖关系,方便的控制执行顺序
  • 设定操作执行的优先级
  • 可以很方便的取消一个操作的执行
  • 使用KVO观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled

基本使用

NSOperation 单独使用时系统同步执行操作,配合 NSOperationQueue 我们能更好的实现异步执行

使用步骤分为三步

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

之后,系统将队列中的操作取出来执行操作

创建操作

上面说了,我们不能直接使用抽象类,而是使用其子类或系统定义的子类

使用子类NSInvocationOperation(Invocation:调用)

 // 1.创建 NSInvocationOperation 对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];

    // 2.调用 start 方法开始执行操作
    [op start];

- (void)task1 {
        NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}

在这里插入图片描述
可以看到,操作实在当前线程执行的,没有开启新线程

我们使用NSThread创建线程后自启动新线程

[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

在新线程执行操作,结果就是其他线程
也就意味着其只能在自己当前所在的线程中执行同步操作

使用子类NSBlockOperation

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
            NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
        }
    }];

    // 2.调用 start 方法开始执行操作
    [op start];

一样 也是在当前线程执行同步操作,在其他线程操作则为其他线程

但是其提供了一个方法
addExecutionBlock:^可以为NSBlockOperation添加额外的操作
这些操作可以在不同的线程并发执行
在这里插入图片描述

使用自定义继承自NSOperation的子类

如果使用系统提供的两个子类不能满足日常需求,我们可以使用自定义继承自NSOperation的子类。可以通过重写main或者start方法来定义自己的NSOperation对象

重写main方法

NSOperationTest *op = [[NSOperationTest alloc] init];
    
    [op start];

自定义的Test类中重写main方法
- (void)main {
    if (!self.isCancelled) {
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1---%@", [NSThread currentThread]);
        }
    }
}

在这里插入图片描述

重写start方法


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

一个道理

创建队列

  • 主队列:在主线程中执行
// 主队列获取方法
NSOperationQueue *queue = [NSOperationQueue mainQueue];
  • 自定义队列:自动放到子线程中执行,同时包含了串行、并发的功能?(什么串行???)
// 自定义队列创建方法
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

将操作加入到队列中

两种方法

  1. addOperation:将操作加入队列中能够开启新线程,进行并发执行两个任务
    在这里插入图片描述
    在这里插入图片描述

  2. addOperationWithBlock:将操作加入到操作队列后能够开启新线程,进行并发执行
    在这里插入图片描述

NSOperationQueue控制串行、并发

NSOperationQueue有个属性maxConcurrentOperationCount,最大并发数
用来控制一个队列中可以有多少个操作同时参与并发

  • 关于最大并发数maxConcurrentOperationCount
  • maxConcurrentOperationCount默认为-1,表示可以并发执行
  • maxConcurrentOperationCount为1时,串行队列,只能串行执行
  • maxConcurrentOperationCount大于1,队列为并发队列,

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

NSOperation操作依赖

我们可以添加操作之间的依赖关系,通过操作依赖,我们可以很方便的控制操作之间的操作先后顺序

  • addDependency:(NSOperation *)op;添加依赖,使当前操作依赖于操作op的完成
  • removeDependency:(NSOperation *)op;移除依赖,取消当前操作对操作op的依赖
  • @property (readonly, copy) NSArray <NSOperation *> * dependencies在当前操作开始执行之前完成执行的所有操作对象数组
    在这里插入图片描述

使用addOperations

@property (readonly, copy) NSArray <NSOperation *> * dependencies;

waitUntilFinished为YES
数组中的两个线程并发进行
第三个等待前两个结束之后执行 并且第三个使用的是数组中的最后一个线程
在这里插入图片描述
为NO
不等待 三个新线程
在这里插入图片描述

NSOperation优先级

NSOperation提供了优先级属性(queuePriority)。
我们可以通过setQueuePriority方法来改变当前操作在统一队列中的执行优先级


// 优先级的取值
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
}

前面提到过,对于添加到队列中的操作,首先进入准备就绪的状态,就绪状态取决于操作之间的依赖关系,然后进入就绪状态的操作的开始执行顺序由操作之间的优先级确定(优先级是操作对象自身的属性)

优先级不能取代依赖关系。这也就意味着

假如一个队列中既包含了准备就绪状态的操作(没有任何依赖关系,其就是第一个),又包含了未准备就绪的操作,未准备就绪的操作优先级比准备就绪的操作优先级高,那么虽然准备就绪的操作优先级低,也会优先执行。

也就是官方文档中的:

仅当需要对非相关操作的相对优先级进行分类时,才应使用优先级值。优先级值不应用于实现不同操作对象之间的依赖关系管理

如果优先级一样 就按添加顺序执行(最大线程数为1)
在这里插入图片描述
为什么第一个优先级设置为Low没用,为什么第一个固定与addOperation添加顺序的第一个有关呢
说好的不看添加顺序呢?

NSOperation、NSOperationQueue线程间的通讯

回到主线程

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
            //需要在主线程执行的代码
}];

线程安全

NSLock来加锁

  1. 初始化NSLock对象
    self.lock = [[NSLock alloc] init]
  2. 上锁
    [self.lock lock]
  3. 解锁
    [self.lock unlock]
    在这里插入图片描述

常用属性和方法

NSOperation

  • 取消操作 cancel
  • 判断操作状态方法
    • isFinished判断操作是否已经结束
    • isCancelled判断操作是否取消
    • isExecuting判断操作是否正在运行
    • isReady判断是否处于就绪状态
  • 操作同步
    • waitUntilFinished阻塞当前线程
    • setCompletionBlock:(void (^)(void))block;当前操作完毕之后执行block
    • addDependency添加依赖
    • removeDependency移除依赖
    • @property (readonly, copy) NSArray<NSOperation *> *dependencies;数组存储操作

NSOperationQueue

  • 取消/暂停/恢复队列中的操作
    • cancelAllOperations;取消队列中所有的操作
    • isSuspended判断队列是否处理暂停状态 YES:暂停状态,NO恢复状态
    • setSuspended:(BOOL)b;设置操作的暂停和恢复 YES:暂停,NO:恢复
  • 同步操作
    • waitUntilAllOperationsAreFinished;阻塞当前线程,直到队列中的操作全部完成
  • 添加/获取
    • addOperationWithBlock:(void (^)(void))block向队列中添加一个NSBlockOperation类型的操作对象
    • addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait;添加操作数组,wait标志是否阻塞当前线程知道所有操作结束
    • operations当前在队列中的操作数组
    • operations操作个数
  • 获取队列
    • currentQueue当前队列,如果当前线程不在Queue上,返回nil
    • mainQueue获取主队列
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:21:10  更:2021-08-09 10:22:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 13:47:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码