在iOS开发中,谈到多线程,大家第一时间想到的一定是GCD。GCD固然是一套强大的多线程解决方案,能够解决绝大多数的多线程问题,但是他易于上手难于精通且到处是坑的特点也注定了想熟练使用它有一定的难度。而且很多人嘴上天天挂着GCD,实际上对它的实际应用也不甚了解。
再者说,在现在的主流开发模式下,能用到多线程的绝大多数就是网络数据请求和网络图片加载,这两点上AFNetwork+SDWebImage已经能满足几乎所有的需求。而剩下的一小部分,简单好用的NSOperation无疑是比GCD更有优势的。
因此,如果你还是坚持『GCD大法好』,那看到这里就不必再看了。如果你想试一试更简单的方法,那就随我来吧。


什么是NSOperation?

和GCD一样,NSOperation也是苹果提供给我们的一套多线程解决方案。实际上它也是基于GCD开发的,但是比GCD拥有更强的可控性和代码可读性。
NSOperation是一个抽象基类,基本没有什么实际使用价值。我们使用最多的是系统封装好的NSInvocationOperation和NSBlockOperation。
不过NSOperation一些通用的方法你要知道

 NSOperation * operation = [[NSOperation alloc]init];
//开始执行
[operation start];
//取消执行
[operation cancel];
//执行结束后调用的Block
[operation setCompletionBlock:^{
NSLog(@"执行结束");
}];
使用NSInvocationOperation

NSInvocationOperation的使用方式和给Button添加事件比较相似,需要一个对象和一个Selector。使用方法非常简单。
我们先来写一个方法

 - (void)testNSOperation
{
NSLog(@"我在第%@个线程",[NSThread currentThread]);
}

然后调用它

 //创建
NSInvocationOperation * invo = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(testNSInvocationOperation) object:nil];
//执行
[invo start];

得到这样的执行结果

执行结果

我们可以看到NSInvocationOperation其实是同步执行的,因此单独使用的话,这个东西也没有什么卵用,它需要配合我们后面介绍的NSOperationQueue去使用才能实现多线程调用,所以这里我们只需要记住有这么一个东西就行了。

使用NSBlockOperation
  • 终于到了我们今天的第一个重点
    NSBlockOperation也是NSOperation的子类,支持并发的实行一个或多个block,使用起来简单又方便
    执行以下代码

     NSBlockOperation * blockOperation = [[NSBlockOperation
    blockOperationWithBlock:^{
    NSLog(@"1在第%@个线程",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
    NSLog(@"2在第%@个线程",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
    NSLog(@"3在第%@个线程",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
    NSLog(@"4在第%@个线程",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
    NSLog(@"5在第%@个线程",[NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
    NSLog(@"6在第%@个线程",[NSThread currentThread]);
    }];

    这里我们多执行两次并比较结果

第一次执行的结果

第二次执行的结果

第三次执行的结果
  • 通过三次不同结果的比较,我们可以看到,NSBlockOperation确实实现了多线程。但是我们可以看到,它并非是将所有的block都放到放到了子线程中。通过上面的打印记录我们可以发现,它会优先将block放到主线程中执行,若主线程已有待执行的代码,就开辟新的线程,但最大并发数为4(包括主线程在内)。如果block数量大于了4,那么剩下的Block就会等待某个线程空闲下来之后被分配到该线程,且依然是优先分配到主线程。
  • 另外,同一个block中的代码是同步执行的

为了证明以上猜想,我们为它增加更多block,并给每条block添加两行代码。

  NSBlockOperation * blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1在第%@个线程",[NSThread currentThread]);
NSLog(@"1haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"2在第%@个线程",[NSThread currentThread]);
NSLog(@"2haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"3在第%@个线程",[NSThread currentThread]);
NSLog(@"3haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"4在第%@个线程",[NSThread currentThread]);
NSLog(@"4haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"5在第%@个线程",[NSThread currentThread]);
NSLog(@"5haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"6在第%@个线程",[NSThread currentThread]);
NSLog(@"6haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"7在第%@个线程",[NSThread currentThread]);
NSLog(@"7haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"8在第%@个线程",[NSThread currentThread]);
NSLog(@"8haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"9在第%@个线程",[NSThread currentThread]);
NSLog(@"9haha");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"10在第%@个线程",[NSThread currentThread]);
NSLog(@"10haha");
}]; [blockOperation start];

然后我们看一下执行结果

执行结果

]

  • 我们可以看到,最大并发数为4,使用同一个线程的block一定是等待前一个block的代码全部执行结束后才执行,且同步执行。

关于最大并发数
在刚才的结果中我们看到最大并发数为4,但这个值并不是一个固定值。4是我在模拟器上运行的结果,而如果我使用真机来跑的话,最大并发数始终为2。因此,具体的最大并发数和运行环境也是有关系的。我们不必纠结于这个数字

所以NSBlockOperation也不是一个理想的多线程解决方案,尽管我们可以在第一个block中创建UI,在其他Block做数据处理等操作,但还是感觉哪里不舒服。
别着急,我们继续往下看

自定义NSOperation

是的,你没看错,NSOperation是可以自定义的。如果NSInvocationOperation和NSBlockOperation无法满足你的需求,你可以选择自定义一个NSOperation。
经过上面的分析,我们发现,系统提供的两种NSOperation是一定满足不了我们的需求的。
那我们是不是需要自定义一个NSOperation呢?
答案是,不需要。
自定义NSOperation并不难,但是依然要写不少代码,这违背了我们简单实现多线程的初衷。况且,接下来我会介绍我们今天真正的主角--NSOperationQueue。所以,我打算直接跳过这一个环节。
NSOPerationQueue

简单使用

终于轮到我们今天的主角了。
顾名思义,NSOperationQueue就是执行NSOperation的队列,我们可以将一个或多个NSOperation对象放到队列中去执行。
比如我们上面介绍过的NSInvocationOperation,我们来将它放到队列中来

 //依然调用上面的那个方法
NSInvocationOperation * invo = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(testNSInvocationOperation) object:nil]; NSOperationQueue * queue = [[NSOperationQueue alloc]init];
[queue addOperation:invo];

看一下执行结果

执行结果

现在它已经被放到子线程中执行了

我们把刚才写的NSBlockOperation也加到这个Queue中来

 ...原来的代码
  //[blockOperation start]; [queue addOperation:blockOperation]; 

然后我们再来看执行情况

执行结果

我们看到,NSInvocationOperation 和 NSBlockOperation是异步执行的,NSBlockOperation中的每一个Block也是异步执行且都在子线程中执行,每一个Block内部也依然是同步执行。

是不是简单好用又强大?

放入队列中的NSOperation对象不需要调用start方法,NSOPerationQueue会在『合适』的时机去自动调用

更简单的使用方式

除了上述的将NSOperation添加到队列中的使用方法外,NSOperationQueue提供了一个更加简单的方法,只需以下两行代码就能实现多线程调用

    NSOperationQueue * queue = [[NSOperationQueue alloc]init];
[queue addOperationWithBlock:^{
//这里是你想做的操作
}];

你可以同时添加一个或这个多个Block来实现你的操作
怎么样,是不是简单的要死?
(原来这篇文章只需要看这两句就行了是嘛?

还在用GCD?来看看NSOperation吧的更多相关文章

  1. NSOperation GCD 对比 (附NSOperation演练)

    项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好子类化NSOperation的设计思路,是具有面向对象的优点(复用.封装),使得 ...

  2. iOS多线程开发--NSThread NSOperation GCD

    多线程 当用户播放音频.下载资源.进行图像处理时往往希望做这些事情的时候其他操作不会被中 断或者希望这些操作过程中更加顺畅.在单线程中一个线程只能做一件事情,一件事情处理不完另一件事就不能开始,这样势 ...

  3. Swift中GCD与NSOperation相关

    GCD Swift 3必看:从使用场景了解GCD新API 常用写法: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_ ...

  4. [置顶] 阿里IOS面试题之多线程选用NSOperation or GCD

    今天早上接到了阿里从杭州打过来的电话面试.虽然近期面试了一些大中型的互联网企业,但是跟素有“IT界的黄浦军校”的阿里面试官接触还是不免紧张. 面试持续了三四十分钟吧,大部分问题都是简历上的项目经验而来 ...

  5. iOS多线程之GCD详解

    GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制.也是目前苹果官方推荐的多线程开发方法.iOS三种多线程开发中GCD是抽象层次最高的.当然用起来也是最简单的. ...

  6. iOS多线程 NSThread/GCD/NSOperationQueue

    无论是GCD,NSOperationQueue或是NSThread, 都没有线程安全 在需要同步的时候需要使用NSLock或者它的子类进行加锁同步 "] UTF8String], DISPA ...

  7. Object-C关于GCD多线程的使用

    ```objc1 使用Crearte函数创建的并发队列和全局并发队列的主要区别: 1)全局并发队列在整个应用程序中本身是默认存在的并且对应有高优先级.默认优先级.低优先级和后台优先级一共四个并发队列, ...

  8. GCD: 基本概念和Dispatch Queue 【转】

    什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像 NSOperationQueue,他们都允 ...

  9. GCD详解

    什么是GCD? Grand Central Dispatch或者GCD,是一套低层API,提供了一种新的方法来进行并发程序编写.从基本功能上讲,GCD有点像 NSOperationQueue,他们都允 ...

随机推荐

  1. 怎么使用PHPMailer实现邮件的发送??

    来源:http://www.ido321.com/1103.html 发送邮件是常用的功能,LZ今天在项目中也碰到了,特此分享一下. 首先,去下载PHPMailer 1.https://github. ...

  2. php环境配置中各个模块在网站建设中的功能

    上一篇配置环境的时候,我们注意到,有四个模块需要配置,那么,这四个模块分别有哪些功能呢?   一.php php是我们的用来创建动态网页的强有力的脚本语言,安装过程中我们直接解压到某一个路径就好了,比 ...

  3. H264编码参数的一些小细节

    一次写播放器,基于ijkplayer.在播放一些网络视频的时候,发现无论怎么转码,视频比例始终不对.即便获取了分辨率,但是播放的时候,view不是分辨率比例的那个长宽比.使用ffmpeg查看了一下属性 ...

  4. homework-02 最大子区域和

    题目描述 题目建立上一个作业的题目基础上,上一次作业是要求在一个一维序列里找一个最大连续子串,这次task最基础的要求是在一个二维表里找一个最大连续子矩形,但是这次作业有若干个升级版,分别要求可以加入 ...

  5. snprintf 返回值

    在平时写代码的过程中,我一个推荐带有n系列的字符串函数,如 strcat ->strncat sprintf->snprintf 我们有类似的一个函数 void dump_kid(std: ...

  6. 【转】iOS开发工具系列(按功能分)

    http://www.cocoachina.com/newbie/basic/2014/0417/8187.html 这是我们多篇iOS开发工具系列篇中的一篇,此前的文章比如:那些不能错过的Xcode ...

  7. 使用gson进行json转换

    Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库.可以将一个 JSON 字符串转成一个 Java 对象,或者反过来. 示例代码如下: 实体定义 ...

  8. Codeforces Round #337 (Div. 2) D. Vika and Segments (线段树+扫描线+离散化)

    题目链接:http://codeforces.com/contest/610/problem/D 就是给你宽度为1的n个线段,然你求总共有多少单位的长度. 相当于用线段树求面积并,只不过宽为1,注意y ...

  9. KVM学习笔记

    检查机器是否启用KVM lsmod |grep kvm 安装KVM yum install libvirt python-virtinst qemu-kvm virt-viewer bridge-ut ...

  10. C#学习笔记(八):扩展方法

    还记得第一次使用DOTween时,发现缓动方法竟然是可以直接用Transform对象中调用到,当时就被震撼到了(那是还是C#小白一只).好了不多说了,今天来学习一下C#的这个特性——扩展方法. 扩展方 ...