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


什么是NSOperation?

和GCD一样,NSOperation也是苹果提供给我们的一套多线程解决方案。实际上它也是基于GCD开发的,但是比GCD拥有更强的可控性和代码可读性。
NSOperation是一个抽象基类,基本没有什么实际使用价值。我们使用最多的是系统封装好的NSInvocationOperationNSBlockOperation
不过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是可以自定义的。如果NSInvocationOperationNSBlockOperation无法满足你的需求,你可以选择自定义一个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. 【和我一起学python吧】初学Python,版本如何选择?

    早在四年多以前,在我进入英才网之前,去面试过一家海归创业的公司.他们需要的是有unix开发经验的技术人员,但是因为他们当时所处的阶段对很多成熟 技术人员不是很吸引,所以条件放宽为熟悉面向对象的程序开发 ...

  2. ACCESS TOKEN

    Access Token 在微信公众平台接口开发中,Access Token占据了一个很重要的地位,相当于进入各种接口的钥匙,拿到这个钥匙才有调用其他各种特殊接口的权限. access_token是公 ...

  3. 各个版本spring的jar包以及源码下载地址

    各个版本spring的jar包以及源码下载地址,目前最高版本到spring4.1.2,留存备用: http://maven.springframework.org/release/org/spring ...

  4. POJ 3159 Candies(差分约束,最短路)

    Candies Time Limit: 1500MS   Memory Limit: 131072K Total Submissions: 20067   Accepted: 5293 Descrip ...

  5. Spring Auto scanning components

    Normally you declare all the beans or components in XML bean configuration file, so that Spring cont ...

  6. JavaScript随笔记(一)基础概念以及变量类型

    一.JavaScript中的基础概念 1.defer属性 一般我们在引用外部js文件的时候往往是将引用文件的位置放在标签当中,比如那么在标签中引入多个js文件时,浏览器会按照引入顺序加载执行这些引入的 ...

  7. Unity3D之UGUI学习笔记(一):UGUI介绍以及Canvas

    UGUI是Unity3D4.6官方提供的UI系统,支持2D和3D UI的开发. Unity3D UI史 OnGUI 在Unity4.6之前,官方提供的是OnGUI函数来开发UI界面,当然问题也比较多, ...

  8. C++成员变量、构造函数的初始化顺序

    一.C++成员变量初始化 1.普通的变量:一般不考虑啥效率的情况下 可以在构造函数中进行赋值.考虑一下效率的可以再构造函数的初始化列表中进行 2.static 静态变量(本地化数据和代码范围): st ...

  9. CSS基础(02)

    CSS 选择器 1.CSS3 选择器简介 在 CSS 中,选择器是一种模式,用于选择需要添加样式的元素. 语法: 下面中"CSS" 列指示该属性是在哪个 CSS 版本中定义的.(C ...

  10. Codeforces Gym 100342H Problem H. Hard Test 构造题,卡迪杰斯特拉

    Problem H. Hard TestTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100342/at ...