Working with the NSOperationQueue Class
Multi-tasking prevents apps from freezing. In most programming languages, achieving this is a bit tricky, but the NSOperationQueue class in iOS makes it easy!
This tutorial will demonstrate how to use the NSOperationQueue class. An NSOperationQueue object is a queue that handles objects of the NSOperationclass type. An NSOperation object, simply phrased, represents a single task, including both the data and the code related to the task. The NSOperationQueue handles and manages the execution of all the NSOperation objects (the tasks) that have been added to it. The execution takes place with the main thread of the application. When an NSOperation object is added to the queue it is executed immediately and it does not leave the queue until it is finished. A task can be cancelled, but it is not removed from the queue until it is finished. The NSOperation class is an abstract one so it cannot be used directly in the program. Instead, there are two provided subclasses, the NSInvocationOperation class and theNSBlockOperation class. I'll use the first one in this tutorial.
The Sample Project
Here's the goal for this tutorial: for each extra thread we want our application to create an NSInvocationOperation (NSOperation) object. We'll add each object into the NSOperationQueue and then we're finished. The queue takes charge of everything and the app works without freezing. To demonstrate clearly the use of the classes I mentioned above, we will create a (simple) sample project in which, apart from the main thread of the app, we will have two more threads running along with it. On the first thread, a loop will run from 1 to 10,000,000 and every 100 steps a label will be updated with the loop's counter value. On the second thread, a label's background will fill with a custom color. That process will take place inside a loop and it will be executed more than once. So we will have something like a color rotator. At the same time, the RGB values of the custom background color along with the loop counter's value will be displayed next to the label. Finally, we will use three buttons to change the view's background color on the main thread. These tasks could not be executed simultaneously without multi-tasking. Here is a look at the end result:

Step 1: Create the Project
Let's begin by creating the project. Open the Xcode and create a new Single View Application.

Click on Next and set a name for the project. I named it ThreadingTestApp. You can use the same or any other name you like.

Next. complete the project creation.

Step 2: Setup the Interface
Click on the ViewController.xib file to reveal the Interface Builder. Add the following controls to create an interface like the next image:

- UINavigationBar
- Frame (x, y, W, H): 0, 0, 320, 44
- Tintcolor: Black color
- Title: "Simple Multi-Threading Demo"
- UILabel
- Frame (x, y, W, H): 20, 59, 280, 21
- Text: "Counter at Thread #1"
- UILabel
- Frame (x, y, W, H): 20, 88, 280, 50
- Background color: Light gray color
- Text color: Dark gray color
- Text: -
- UILabel
- Frame (x, y, W, H): 20, 154, 280, 21
- Text: "Random Color Rotator at Thread #2"
- UILabel
- Frame (x, y, W, H): 20, 183, 100, 80
- Background color: Light gray color
- Text: -
- UILabel
- Frame (x, y, W, H): 128, 183, 150, 80
- Text: -
- UILabel
- Frame (x, y, W, H): 20, 374, 280, 21
- Text: "Background Color at Main Thread"
- UIButton
- Frame (x, y, W, H): 20, 403, 73, 37
- Title: "Color #1"
- UIButton
- Frame (x, y, W, H): 124, 403, 73, 37
- Title: "Color #2"
- UIButton
- Frame (x, y, W, H): 228, 403, 73, 37
- Title: "Color #3"
For the last UILabel and the three UIButtons, set the Autosizing value to Left - Bottom to make the interface look nice on the iPhone 4/4S and iPhone 5, just like the next image:

Step 3: IBOutlet Properties and IBAction Methods
In this next step we will create the IBOutlet properties and IBAction methods that are necessary to make our sample app work. To create new properties and methods, and connect them to your controls while being the Interface Builder, click on the middle button of the Editor button at the Xcode toolbar to reveal the Assistant Editor:

Not every control needs an outlet property. We will add only one for the UILabels 3, 5, and 6 (according to the order they were listed in step 2), named label1, label2, and label3.
To insert a new outlet property, Control+Click (Right click) on a label > Click on the New Referencing Outlet > Drag and Drop into the Assistant Editor. After that, specify a name for the new property, just like in the following images:
Inserting a new IBOutlet property

Setting the IBOutlet property name
Repeat the process above three times to connect the three UILabels to properties. Inside your ViewController.h file you have these properties declared:
|
1
2
3
|
@property (retain, nonatomic) IBOutlet UILabel *label1;@property (retain, nonatomic) IBOutlet UILabel *label2;@property (retain, nonatomic) IBOutlet UILabel *label3; |
Now add the IBAction methods for the three UIButtons. Each one button will change the background color of the view. To insert a new IBAction method, Control+Click (Right click) on a UIButton > Click on the Touch Up Inside > Drag and Drop into the Assistant Editor. After that specify a name for the new method. Take a look at the following images and the next snippet for the method names:

Inserting a new IBAction method

Setting the IBAction method name
Again, repeat the process above three times to connect every UIButton to an action method. The ViewController.h file should now contain these:
|
1
2
3
|
- (IBAction)applyBackgroundColor1;- (IBAction)applyBackgroundColor2;- (IBAction)applyBackgroundColor3; |
The IBOutlet properties and IBAction methods are now ready. We can now begin coding.
Step 4: The NSOperationQueue Object and the Necessary Task-Related Method Declarations
One of the most important tasks we must do is to declare a NSOperationQueueobject (our operation queue), which will be used to execute our tasks in secondary threads. Open the ViewController.h file and add the following content right after the @interface header (don't forget the curly brackets):
|
1
2
3
|
@interface ViewController : UIViewController{ NSOperationQueue *operationQueue;} |
Also, each task needs to have at least one method which contains the code that will run simultaneously with the main thread. According to the introductory description, the first task the method will be named counterTask and the second one will be named colorRotatorTask:
|
1
2
|
-(void)counterTask;-(void)colorRotatorTask; |
That's all we need. Our ViewController.h file should look like this:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@interface ViewController : UIViewController{ NSOperationQueue *operationQueue;}@property (retain, nonatomic) IBOutlet UILabel *label1;@property (retain, nonatomic) IBOutlet UILabel *label2;@property (retain, nonatomic) IBOutlet UILabel *label3;- (IBAction)applyBackgroundColor1;- (IBAction)applyBackgroundColor2;- (IBAction)applyBackgroundColor3;-(void)counterTask;-(void)colorRotatorTask;@end |
Let's move on to implementation.
Step 5: Implementation
We're almost finished. We have setup our interface, made all the necessary connections, declared any needed IBAction and other methods, and established our base. Now it is time to build upon them.
Open the ViewController.m file and go to the viewDidLoad method. The most important part of this tutorial is going to take place here. We will create a newNSOperationQueue instance and two NSOperation (NSInvocationOperation)objects. These objects will encapsulate the code of the two methods we previously declared and then they will be executed on their own by the NSOperationQueue. Here is the code:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
- (void)viewDidLoad{ [super viewDidLoad]; // Create a new NSOperationQueue instance. operationQueue = [NSOperationQueue new]; // Create a new NSOperation object using the NSInvocationOperation subclass. // Tell it to run the counterTask method. NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(counterTask) object:nil]; // Add the operation to the queue and let it to be executed. [operationQueue addOperation:operation]; [operation release]; // The same story as above, just tell here to execute the colorRotatorTask method. operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(colorRotatorTask) object:nil]; [operationQueue addOperation:operation]; [operation release];} |
This whole process is really simple. After creating the NSOperationQueue instance, we create an NSInvocationOperation object (operation). We set its selector method (the code we want executed on a separate thread), and then we add it to the queue. Once it enters the queue it immediately begins to run. After that the operation object can be released, since the queue is responsible for handling it from now on. In this case we create another object and we'll use it the same way for the second task (colorRotatorTask).
Our next task is to implement the two selector methods. Let's begin by writing thecounterTask method. It will contain a for loop that will run for a large number of iterations and every 100 steps the label1's text will be updated with the current iteration's counter value (i). The code is simple, so here is everything:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
-(void)counterTask{ // Make a BIG loop and every 100 steps let it update the label1 UILabel with the counter's value. for (int i=0; i<10000000; i++) { if (i % 100 == 0) { // Notice that we use the performSelectorOnMainThread method here instead of setting the label's value directly. // We do that to let the main thread to take care of showing the text on the label // and to avoid display problems due to the loop speed. [label1 performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"%d", i] waitUntilDone:YES]; } } // When the loop gets finished then just display a message. [label1 performSelectorOnMainThread:@selector(setText:) withObject:@"Thread #1 has finished." waitUntilDone:NO];} |
Please note that it is recommended as the best practice (even by Apple) to perform any visual updates on the interface using the main thread and not by doing it directly from a secondary thread. Therefore, the use of the performSelectorOnMainThreadmethod is necessary in cases such as this one.
Now let's implement the colorRotatorTask method:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
-(void)colorRotatorTask{ // We need a custom color to work with. UIColor *customColor; // Run a loop with 500 iterations. for (int i=0; i<500; i++) { // Create three float random numbers with values from 0.0 to 1.0. float redColorValue = (arc4random() % 100) * 1.0 / 100; float greenColorValue = (arc4random() % 100) * 1.0 / 100; float blueColorValue = (arc4random() % 100) * 1.0 / 100; // Create our custom color. Keep the alpha value to 1.0. customColor = [UIColor colorWithRed:redColorValue green:greenColorValue blue:blueColorValue alpha:1.0]; // Change the label2 UILabel's background color. [label2 performSelectorOnMainThread:@selector(setBackgroundColor:) withObject:customColor waitUntilDone:YES]; // Set the r, g, b and iteration number values on label3. [label3 performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"Red: %.2f\nGreen: %.2f\nBlue: %.2f\Iteration #: %d", redColorValue, greenColorValue, blueColorValue, i] waitUntilDone:YES]; // Put the thread to sleep for a while to let us see the color rotation easily. [NSThread sleepForTimeInterval:0.4]; } // Show a message when the loop is over. [label3 performSelectorOnMainThread:@selector(setText:) withObject:@"Thread #2 has finished." waitUntilDone:NO];} |
You can see that we used the performSelectorOnMainThread method here as well. The next step is the [NSThread sleepForTimeInterval:0.4]; command, which is used to cause some brief delay (0.4 seconds) in each loop execution. Even though it is not necessary to use this method, it is preferable to use it here to slow down the background color's changing speed of the label2 UILabel (our color rotator). Additionally in each loop we create random values for the red, green, and blue. We then set these values to produce a custom color and set it as a background color in the label2 UILabel.
At this point the two tasks that are going to be executed at the same time with the main thread are ready. Let's implement the three (really easy) IBAction methods and then we are ready to go. As I have already mentioned, the three UIButtons will change the view's background color, with the ultimate goal to demonstrate how the main thread can run alongside the other two tasks. Here they are:
|
01
02
03
04
05
06
07
08
09
10
11
|
- (IBAction)applyBackgroundColor1 { [self.view setBackgroundColor:[UIColor colorWithRed:255.0/255.0 green:204.0/255.0 blue:102.0/255.0 alpha:1.0]];}- (IBAction)applyBackgroundColor2 { [self.view setBackgroundColor:[UIColor colorWithRed:204.0/255.0 green:255.0/255.0 blue:102.0/255.0 alpha:1.0]];}- (IBAction)applyBackgroundColor3 { [self.view setBackgroundColor:[UIColor whiteColor]];} |
That's it! Now you can run the application and see how three different tasks can take place at the same time. Remember that when the execution of NSOperation objects is over, it will automatically leave the queue.
Conclusion
Many of you may have already discovered that the actual code to run a multi-tasking app only requires a few lines of code. It seems that the greatest workload is implementing the required methods that work with each task. Nevertheless, this method is an easy way to develop multi-threading apps in iOS.
Working with the NSOperationQueue Class的更多相关文章
- Cocoa深入学习:NSOperationQueue、NSRunLoop和线程安全 (转)
目前在 iOS 和 OS X 中有两套先进的同步 API 可供我们使用:NSOperation 和 GCD .其中 GCD 是基于 C 的底层的 API ,而 NSOperation 则是 GCD 实 ...
- 多线程下NSOperation、NSBlockOperation、NSInvocationOperation、NSOperationQueue的使用
本篇文章主要介绍下多线程下NSOperation.NSBlockOperation.NSInvocationOperation.NSOperationQueue的使用,列举几个简单的例子. 默认情况下 ...
- NSOperationQueue的其他方法
1.设置最大并发数 什么是并发数 同时执行的任务数 比如,同时开3个线程执行3个任务,并发数就是3 最大并发数的相关方法 - (NSInteger)maxConcurrentOperationCo ...
- NSOperationQueue的基本使用
NSOperationQueue的作用 NSOperation可以调用start方法来执行任务,但默认是同步执行的 如果将NSOperation添加到NSOperationQueue(操作队列)中,系 ...
- NSOperationQueue与GCD的使用原则和场景
首先,我们要明确NSOperationQueue与GCD之间的关系: NSOpertaionQueue用GCD构建封装的,是GCD的高级抽象. 其次,我们要区别两者的不同: GCD仅仅支持FIFO队列 ...
- 伟大的GCD和NSOperationQueue
一. GCD GCD中最重要的两个东西 任务 和 队列 任务就是一段代码(用来缓存,下载,计算等操作) 队列从大的方面分为两个队列:主队列(串行队列)和 自己创建的队列(串行,和并行) 主队列中: 在 ...
- 多线程NSInvocationOperation(NSOperationQueue)的基本用法
#import "ViewController.h" @interface ViewController () @end @implementation ViewContr ...
- IOS 多线程02-pthread 、 NSThread 、GCD 、NSOperationQueue、NSRunLoop
注:本人是翻译过来,并且加上本人的一点见解. 要点: 1.前言 2.pthread 3.NSThread 4.Grand Central Dispatch(GCD) 5.Operation Queue ...
- GCD与NSOperationQueue
1> GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装 2> GCD只支持FIFO(先入先出)的队列,NSOperationQueue可以很方便地调整执 ...
- 多线程编程4 - NSOperationQueue
一.简介 一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的.也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步 ...
随机推荐
- android配置开发环境
1.下载Java SE并安装. 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 配置环境变量 我的电脑- ...
- 【bzoj1179】[Apio2009]Atm Tarjan缩点+Spfa最长路
题目描述 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每 ...
- hihoCoder #1902 字符替换
解法 这题比赛时过的人很多,我却没思路,糊里糊涂写了个强联通分量,得了 80 分. 这题思路是这样的. 一个替换操作可以看做一个有向边,所以题目实际上给出了一个有向图 $G$,一个节点代表一个字母. ...
- Linux下从零开始部署和使用Jaeger
最近在折腾Jaeger,Jaeger官网都是介绍如何通过Docker部署,二进制部署文档基本没有(已咨询过作者,作者说没文档!你参考Docker自己部署好了!!!),所以打算写一篇Linux部署. J ...
- POJ 1064 Cable master | 二分+精度
题目: 给n个长度为l[i](浮点数)的绳子,要分成k份相同长度的 问最多多长 题解: 二分长度,控制循环次数来控制精度,输出也要控制精度<wa了好多次> #include<cstd ...
- Lights inside 3D Grid LightOJ - 1284 (概率dp + 推导)
Lights inside 3D Grid LightOJ - 1284 题意: 在一个三维的空间,每个点都有一盏灯,开始全是关的, 现在每次随机选两个点,把两个点之间的全部点,开关都按一遍:问k次过 ...
- BZOJ5154 [Tjoi2014]匹配 【KM算法 + 枚举】
题目链接 BZOJ5154 题解 先跑出一个匹配方案 然后暴力删去每对匹配再检验一下答案是否减小 使用KM算法提升速度 #include<algorithm> #include<io ...
- HTML标签的使用要注意语义化
语义化标签:你认为用什么标签最能描述这块内容,觉得这样表述更有意义,那么就可以使用这个标签. 现在的浏览器对CSS支持都挺完善的(不包括CSS3),讲究的是结构与表现相分离,结构与行为相分离,一个WE ...
- 关闭vscode打开新文件自动关闭预览文件功能
经常碰到这个问题,我打开文件就是有用的,每次给我自动关闭了我还得去打开. 当然这个问题可以双击文件,接触那个文件的预览状态就可以解决了.不过还有一个更懒的方法,直接修改vscode配置就好了. // ...
- WCF信道绑定代码
监听端创建信道Listener,代码 using System; using System.Collections.Generic; using System.Linq; using System.T ...