NSOperation 抽象类

  • NSOperation 是一个”抽象类”,不能直接使用
  • 抽象类的用处是定义子类共有的属性和方法
  • 在苹果的头文件中,有些抽象类和子类的定义是在同一个头文件中的
  • 子类:
    • NSInvocationOperation (调用)
    • NSBlockOperation (块)
  • NSOperationQueue 队列

已经学习过的抽象类

  • UIGestureRecognizer
  • CAAnimation
  • CAPropertyAnimation

基本演练

NSInvocationOperation

start

  • start 方法 会在当前线程执行 @selector 方法
- (void)opDemo1 {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"Invocation"]; // start方法 会在当前线程执行 @selector 方法
[op start];
} - (void)downloadImage:(id)obj { NSLog(@"%@ %@", [NSThread currentThread], obj);
}

添加到队列

  • 将操作添加到队列,会”异步”执行 selector 方法
- (void)opDemo2 {
NSOperationQueue *q = [[NSOperationQueue alloc] init]; NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"queue"]; [q addOperation:op];
}

添加多个操作

- (void)opDemo3 {
NSOperationQueue *q = [[NSOperationQueue alloc] init]; for (int i = 0; i < 10; ++i) {
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@(i)]; [q addOperation:op];
}
}

执行效果:会开启多条线程,而且不是顺序执行。与GCD中并发队列&异步执行效果一样!

结论,在 NSOperation 中:

  • 操作 -> 异步执行的任务
  • 队列 -> 全局队列

NSBlockOperation

- (void)opDemo4 {
NSOperationQueue *q = [[NSOperationQueue alloc] init]; NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}]; [q addOperation:op];
}

使用 block 来定义操作,所有的代码写在一起,更简单,便于维护!

更简单的,直接添加 Block

- (void)opDemo5 {
NSOperationQueue *q = [[NSOperationQueue alloc] init]; for (int i = 0; i < 10; ++i) {
[q addOperationWithBlock:^{
NSLog(@"%@ %d", [NSThread currentThread], i);
}];
}
}

向队列中添加不同的操作

- (void)opDemo5 {
NSOperationQueue *q = [[NSOperationQueue alloc] init]; for (int i = 0; i < 10; ++i) {
[q addOperationWithBlock:^{
NSLog(@"%@ %d", [NSThread currentThread], i);
}];
} NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block %@", [NSThread currentThread]);
}];
[q addOperation:op1]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage:) object:@"invocation"];
[q addOperation:op2];
}
  • 可以向 NSOperationQueue 中添加任意 NSOperation 的子类

线程间通讯

- (void)opDemo6 {
NSOperationQueue *q = [[NSOperationQueue alloc] init]; [q addOperationWithBlock:^{
NSLog(@"耗时操作 %@", [NSThread currentThread]); // 主线程更新 UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"更新 UI %@", [NSThread currentThread]);
}];
}];
}

高级演练

全局队列

/// 全局操作队列,统一管理所有的异步操作
@property (nonatomic, strong) NSOperationQueue *queue; - (NSOperationQueue *)queue {
if (_queue == nil) {
_queue = [[NSOperationQueue alloc] init];
}
return _queue;
}

最大并发操作数

/// MARK: - 最大并发操作数
- (void)opDemo1 { // 设置同时并发操作数
self.queue.maxConcurrentOperationCount = 2; NSLog(@"start"); for (int i = 0; i < 10; ++i) {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.0];
NSLog(@"%@ %d", [NSThread currentThread], i);
}]; [self.queue addOperation:op];
}
}

暂停 & 继续

/// MARK: - 暂停 & 继续
- (IBAction)pauseAndResume { if (self.queue.operationCount == 0) {
NSLog(@"没有操作");
return;
} // 暂停或者继续
self.queue.suspended = !self.queue.isSuspended; if (self.queue.isSuspended) {
NSLog(@"暂停 %tu", self.queue.operationCount);
} else {
NSLog(@"继续 %tu", self.queue.operationCount);
}
}
  • 队列挂起,当前”没有完成的操作”,是包含在队列的操作数中的
  • 队列挂起,不会影响已经执行操作的执行状态
  • 对列一旦被挂起,再添加的操作不会被调度

取消全部操作

/// MARK: - 取消所有操作
- (IBAction)cancelAll {
if (self.queue.operationCount == 0) {
NSLog(@"没有操作");
return;
} // 取消对列中的所有操作,同样不会影响到正在执行中的操作!
[self.queue cancelAllOperations]; NSLog(@"取消全部操作 %tu", self.queue.operationCount);
}
  • 取消队列中所有的操作
  • 不会取消正在执行中的操作
  • 不会影响队列的挂起状态

依赖关系

/// MARK: - 依赖关系
- (void)dependency { NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"登录 %@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"付费 %@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载 %@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"通知用户 %@", [NSThread currentThread]);
}]; [op2 addDependency:op1];
[op3 addDependency:op2];
[op4 addDependency:op3];
// 注意不要循环依赖
// [op1 addDependency:op4]; [self.queue addOperations:@[op1, op2, op3] waitUntilFinished:NO];
[[NSOperationQueue mainQueue] addOperation:op4]; NSLog(@"come here");
}

与 GCD 的对比

  • GCD

    • 任务(block)添加到队列(串行/并发/主队列),并且指定任务执行的函数(同步/异步)
    • GCD是底层的C语言构成的API
    • iOS 4.0 推出的,针对多核处理器的并发技术
    • 在队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构
    • 要停止已经加入 queueblock 需要写复杂的代码
    • 需要通过 Barrier 或者同步任务设置任务之间的依赖关系
    • 只能设置队列的优先级
    • 高级功能:
      • 一次性 once
      • 延迟操作 after
      • 调度组
  • NSOperation

    • 核心概念:把操作(异步)添加到队列(全局的并发队列)
    • OC 框架,更加面向对象,是对 GCD 的封装
    • iOS 2.0 推出的,苹果推出 GCD 之后,对 NSOperation 的底层全部重写
    • Operation作为一个对象,为我们提供了更多的选择
    • 可以随时取消已经设定要准备执行的任务,已经执行的除外
    • 可以跨队列设置操作的依赖关系
    • 可以设置队列中每一个操作的优先级
    • 高级功能:
      • 最大操作并发数(GCD不好做)
      • 继续/暂停/全部取消
      • 跨队列设置操作的依赖关系

自定义操作

准备工作

  • 自定义 DownloadImageOperation 继承自 NSOperation
  • 代码调用
// 实例化自定义操作
DownloadImageOperation *op = [[DownloadImageOperation alloc] init];
// 将自定义操作添加到下载队列
[self.downloadQueue addOperation:op];

需求驱动开发

目标一:设置自定义操作的执行入口

对于自定义操作,只要重写了 main 方法,当队列调度操作执行时,会自动运行 main 方法

注意main 方法中需要使用自动释放池!

- (void)main {
@autoreleasepool {
NSLog(@"%@", [NSThread currentThread]);
}
}

目标二:给自定义参数传递参数

  • 定义属性
/// 要下载图像的 URL 字符串
@property (nonatomic, copy) NSString *URLString;
  • 代码调用
// 实例化自定义操作
DownloadImageOperation *op = [[DownloadImageOperation alloc] init];
// 设置操作属性
op.URLString = @"https://www.baidu.com/img/bdlogo.png"; // 将自定义操作添加到下载队列,操作启动后会执行 main 方法
[self.downloadQueue addOperation:op];

注意,main 方法被调用时,属性已经准备就绪

目标三:如何回调?

利用系统提供的 CompletionBlock 属性

// 设置完成回调
[op setCompletionBlock:^{
NSLog(@"完成 %@", [NSThread currentThread]);
}];
  • 只要设置了 CompletionBlock,当操作执行完毕后,就会被自动调用
  • CompletionBlock 既不在主线程也不在操作执行所在线程
  • CompletionBlock 无法传递参数

自己定义回调 Block,在操作结束后执行

  • 定义属性
/// 完成回调 Block
@property (nonatomic, copy) void (^finishedBlock)(UIImage *image);
  • 设置自定义回调
// 设置自定义完成回调
[op setFinishedBlock:^(UIImage *image) {
NSLog(@"finished %@ %@", [NSThread currentThread], image);
}];
  • 耗时操作后执行回调
// 判断自定义回调是否存在
if (self.finishedBlock != nil) {
// 通常为了简化调用方的代码,异步操作结束后的回调,大多在主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.finishedBlock(@"hello");
}];
}

目标四:简化操作创建

  • 定义方法
///  实例化下载图像操作
///
/// @param URLString 图像 URL 字符串
/// @param finished 完成回调 Block
///
/// @return 下载操作实例
+ (instancetype)downloadImageOperationWithURLString:(NSString *)URLString finished:(void (^)(UIImage *image))finished;
  • 实现方法
+ (instancetype)downloadImageOperationWithURLString:(NSString *)URLString finished:(void (^)(UIImage *))finished {
DownloadImageOperation *op = [[DownloadImageOperation alloc] init]; op.URLString = URLString;
op.finishedBlock = finished; return op;
}
  • 方法调用
// 使用类方法实例化下载操作
DownloadImageOperation *op = [DownloadImageOperation downloadImageOperationWithURLString:@"http://www.baidu.com/img/bdlogo.png" finished:^(UIImage *image) {
NSLog(@"%@", image);
}]; // 将自定义操作添加到下载队列,操作启动后会执行 main 方法
[self.downloadQueue addOperation:op];

目标五:取消操作

在关键节点添加 isCancelled 判断

  • 添加多个下载操作
for (int i = 0; i < 10; ++i) {
NSString *urlString = [NSString stringWithFormat:@"http://www.xxx.com/%04d.png", i]; DownloadImageOperation *op = [DownloadImageOperation downloadImageOperationWithURLString:urlString finished:^(UIImage *image) {
NSLog(@"===> %@", image);
}]; // 将自定义操作添加到下载队列,操作启动后会执行 main 方法
[self.downloadQueue addOperation:op];
}
  • 设置队列最大并发操作数
_downloadQueue.maxConcurrentOperationCount = 2;
  • 内存警告时取消所有操作
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; [self.downloadQueue cancelAllOperations];
}

cancelAllOperations 会向队列中的所有操作发送 Cancel 消息

  • 调整 main 方法,在关键节点判断
- (void)main {
NSLog(@"%s", __FUNCTION__); @autoreleasepool { NSLog(@"下载图像 %@", self.URLString);
// 模拟延时
[NSThread sleepForTimeInterval:1.0]; if (self.isCancelled) {
NSLog(@"1.--- 返回");
return;
} // 判断自定义回调是否存在
if (self.finishedBlock != nil) {
// 通常为了简化调用方的代码,异步操作结束后的回调,大多在主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.finishedBlock(self.URLString);
}];
}
}
} - (void)start {
[super start]; NSLog(@"%s", __FUNCTION__);
}

注意:如果操作状态已经是 Cancel,则不会执行 main 函数

  • 队列调度操作时,首先执行 start 方法将线程放入可调度线程池
  • 操作执行时的入口是 main 方法

NSOperation类的更多相关文章

  1. 认识和使用NSOperation

    原文链接:http://www.jianshu.com/p/2de9c776f226 NSOperation是OC中多线程技术的一种,是对GCD的OC包装.它包含队列(NSOperationQueue ...

  2. iOS开发之多线程技术——NSOperation篇

    本篇将从四个方面对iOS开发中使用到的NSOperation技术进行讲解: 一.什么是NSOperation 二.我们为什么使用NSOperation 三.在实际开发中如何使用NSOperation ...

  3. iOS开发多线程篇—NSOperation简单介绍

    iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现 ...

  4. 多线程NSOperation

      NSOperation(经常使用): 1.为什么会有NSOperation?弥补gcd的一些问题:1)下载为例子:如果gcd放到队列中的block操作面对网络有问题,block之外无法取消bloc ...

  5. iOS 并发:NSOperation 与调度队列入门(1)

    一直以来,并发都被视为 iOS 开发中的「洪水猛兽」.许多开发者都将其视为危险地带,唯恐避之而不及.更有谣传认为,多线程代码应该尽力避免.笔者同意,如果你对并发的了解不够深入,就容易造成危险.但是,危 ...

  6. iOS多线程——GCD与NSOperation总结

    很长时间以来,我个人(可能还有很多同学),对多线程编程都存在一些误解.一个很明显的表现是,很多人有这样的看法: 新开一个线程,能提高速度,避免阻塞主线程 毕竟多线程嘛,几个线程一起跑任务,速度快,还不 ...

  7. iOS 开发多线程 —— NSOperation

    本文是根据文顶顶老师的博客学习而来,转载地址:http://www.cnblogs.com/wendingding/p/3809042.html 一.NSOperation简介 1.简单说明 NSOp ...

  8. iOS多线程编程之NSOperation的基本操作(转载)

    一.NSOperation简介 1.简单说明 NSOperation的作⽤:配合使用NSOperation和NSOperationQueue也能实现多线程编程 NSOperation和NSOperat ...

  9. iOS-----使用NSOperation与NSOperationQueue实现多线程

    使用NSOperation与NSOperationQueue实现多线程 NSOperation与NSOperationQueue的基本理论如下. NSOperationQueue 代表一个FIFO的队 ...

随机推荐

  1. jQuery的基础效果题

    Jquery第二次考核 之真金不怕火炼 1.  名词解释 实例对象:var p1=new Person();  p1就是实例对象 构造:function Person(){} 原型对象:在 JavaS ...

  2. 【HNOI2010】弹飞绵羊 - LCT

    题目描述 某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏.游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系 ...

  3. 痞子衡嵌入式:利用i.MXRT1060,1010上新增的FlexSPI地址重映射(Remap)功能可安全OTA

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT部分型号上新增的FlexSPI Remap功能. OTA升级设计几乎是每个量产客户都绕不开的话题,产品发布后免不了要做固件( ...

  4. IDEA常用快捷键Mac os和Windows对照--用到了就会更新

    之前公司用了一段的MacBookPro,离职后自己入手了一台MacBookPro.但是现在的公司中使用的电脑是古老的win7,两个系统的键盘有些许差别,而且快捷键也略有不同.最近因为疫情影响,在家远程 ...

  5. 兼容低版本IE浏览器的一些心得体会(持续更新)

    前言: 近期工作中,突然被要求改别人的代码,其中有一项就是兼容IE低版本浏览器,所以优雅降级吧. 我相信兼容低版本IE是许多前端开发的噩梦,尤其是改别人写的代码,更是痛不欲生. 本文将介绍一些本人兼容 ...

  6. 快速构建一个完整的Selenium框架

    今天跟大家细讲如何构建一个完整的selenium框架,当你学会了这一篇你就也可以说自己会selenium自动化测试了. 1.新建项目,结构如图: 注意:整个项目除了最外层的是文件夹,其他的都是包(pa ...

  7. e3mall商城的归纳总结3之后台商品节点、认识nginx

    一  后台商品节点 大家都知道后台创建商品的时候需要选择商品的分类,而这个商品的分类就就像一棵树一样,一层包含一层又包含一层.因此这里用的框架是easyUiTree.该分类前端使用的是异步加载模式(指 ...

  8. js中使用const声明变量时需要注意

    const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址(初始化的内容)不得改动.对于简单类型的数据(数值.字符串.布尔值),值就保存在变量指向的那个内存地址,因此等同于常量. 简单 ...

  9. 移动开发中如何整合HTML 5和原生代码

    移动开发中如何整合HTML 5和原生代码 https://blog.csdn.net/lvjin110/article/details/41038565

  10. 【平台开发】— 8.前端-从[项目管理]来看vue

    现在要来实现[项目管理]这个功能了. 想象一下页面,元素大概就是:列表页.查询框.新增按钮.编辑.mock入口按钮. 那先来实现列表和新建,也顺带着整理一下用到的vue相关指令知识. 一.后端 后端就 ...