In this article, I’ll take a look at several multithreading options for iOS development, and how to use them and what to look out for. Of course, several multithreading cases will also be given to feel their differences in practical use. Another point to note is that this article will be explained in both Swift and Objective-C languages, bilingual kindergarten. OK, let ‘s begin!

An overview of the

In this article, I won’t talk about what multithreading is, the difference between threads and processes, what multithreading does, and of course I won’t talk about what is serial and what is parallel, which we should all know.

There are actually 4 multithreading schemes in iOS, they are:

  • Pthreads
  • NSThread
  • GCD
  • NSOperation & NSOperationQueue

So next, I will explain how to use these schemes and some cases one by one. Along the way, I’ll also mention some multithreading peripherals. Examples include thread synchronization, delayed execution, singleton patterns, and so on.

Pthreads

In fact, this program needless to say, just to fill the number, in order to let everyone know about it. Baidu Encyclopedia says:

POSIX Threads, or Pthreads for short, is the POSIX standard for threads. The standard defines a set of apis for creating and manipulating threads. On UniX-like operating systems (Unix, Linux, Mac OS X, etc.), Pthreads are used as operating system threads.

In short, it’s a multithreaded API that works on many operating systems, so it’s portable (but not useful), and it works on iOS as well. However, this is based on the C language framework, the use of this acid cool! Feel it:

OBJECTIVE-C

The first step, of course, is to include the header file

#import

The thread is then created and the task is executed

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { pthread_t thread; // Create a thread and automatically execute pthread_create(&thread, NULL, start, NULL); } void *start(void *data) { NSLog(@"%@", [NSThread currentThread]); return NULL; }Copy the code

Printout:

2015-07-27 23:57:21.689 testThread[10616:2644653] {number = 2, name = (null)}

If you look at the code, you can see that it needs c functions, which is a little bit more painful, but even more painful is that you have to manually handle the transitions between the states of the thread and manage the life cycle. For example, this code creates a thread, but it doesn’t destroy it.

SWIFT

Unfortunately, THIS method cannot be implemented in my current Swift1.2 because the function requires passing in a function pointer type CFunctionPointer, which Swift cannot currently convert the method to. Swift 2.0 has introduced a new feature, @convention(c), that can convert swift methods into C Pointers. You can see it here

So, that’s all I have to say about multithreading in Pthreads, because it’s almost impossible to do iOS development. But if you’re interested, or if you want to implement your own multithreaded solution, customize it from the ground up, do some research.

NSThread

The solution is packaged by Apple and fully object-oriented. So you can manipulate thread objects directly, which is very intuitive and convenient. However, its life cycle still needs to be managed manually, so this scheme is also used occasionally, such as [NSThread currentThread], it can get the currentThread class, you can know the various properties of the currentThread, very convenient for debugging. Here’s a look at some of its uses.

Create and start

  • Create the thread class first and then start it

    OBJECTIVE-C
    // create NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run: object:nil); // start [thread start];Copy the code
    SWIFT
    // Create let thread = NSThread(target: self, selector: "run:", object: nil)Copy the code
  • Create and start automatically

    OBJECTIVE-C
      [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];Copy the code
    SWIFT
      NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil)Copy the code
  • Created using the NSObject method and started automatically

    OBJECTIVE-C
      [self performSelectorInBackground:@selector(run:) withObject:nil];Copy the code
    SWIFT

    It’s a pity that too! Apple decided that performSelector was unsafe, so they removed it from Swift.

    Note: The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.

Other methods

In addition to creating and starting, nsThreads have a lot of methods, and I’m going to list some of the common methods, but I’m not going to list them completely, and you can see more of them in the class definition.

OBJECTIVE-C
// Cancel the thread - (void)cancel; // Start thread - (void)start; @property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isCancelled) BOOL cancelled; -(void)setName:(NSString *)n; -(NSString *)name; + (NSThread *)currentThread; + (NSThread *)mainThread; + (void)sleepForTimeInterval:(NSTimeInterval)time; + (void)sleepForTimeInterval:(NSTimeInterval)time; + (void)sleepUntilDate:(NSDate *)date;Copy the code
SWIFT

The Swift method names are the same as the OC method names, so I won’t waste any space listing them.

Actually, NSThread is pretty easy to use, because there are only a few methods. At the same time, we only use NSThreads in very simple scenarios, because they are not smart enough to gracefully handle other advanced concepts in multithreading. So what comes next is important.

GCD

Grand Central Dispatch. It sounds like it. It is apple multicore parallel computing solutions, so automatically reasonably use more CPU kernel, such as dual-core, quad-core), the most important thing is that it will automatically manage the life cycle of a thread (create threads, thread scheduling tasks, destroy), without the need for our management, we just need to tell what to do. It also uses C, but the use of blocks (called closures in Swift) makes it easier and more flexible to use. So basically everyone uses the GCD program, suitable for the old and the young, it is really a necessary medicine for family travel, killing. Sorry, it’s a bit of a double. Let’s move on.

Tasks and queues

In GCD, two very important concepts have been added: tasks and queues.

  • Task: Action, what you want to do, is basically a piece of code, and in GCD it’s a Block, so it’s easy to add tasks. Tasks can be executed in two ways: synchronous and asynchronous, and the difference between them is whether a new thread is created.

    Synchronous execution: Any synchronous task will be executed on the current thread.

    Asynchronous execution: As long as the task is executed asynchronously, another thread is opened and executed in another thread.

    Update: The main difference between sync and async is that it blocks the current thread until the task in the Block completes! In the case of a sync operation, it blocks the current thread and waits for tasks in the Block to complete before the current thread continues. If an async operation is performed, the current thread will proceed directly, and it will not block the current thread.

  • Queue: Used to store tasks. There are two types of queues, serial queues and parallel queues.

    Tasks in a serial queue are executed in FIFO, one after the other, according to the definition of the queue.

    Update: For tasks placed in a serial queue, GCD retrieves one in FIFO, executes one, then removes the next, and so on.

    Tasks in parallel queues can be executed differently depending on whether they are synchronous or asynchronous.

    Update: GCD also fetches tasks in FIFO when they are placed in a serial queue, but the difference is that it fetches tasks in another thread and then fetches tasks in another thread. So, because I’m doing it so fast, I’m ignoring it, and it looks like all the tasks are being done together. Note, however, that GCD controls the amount of parallelism based on system resources, so if there are many tasks, it will not execute them all at once.

Although it is very convoluted, please look at the following table:

Synchronous execution Asynchronous execution
Serial queues Current thread, executed one by one The other threads execute one by one
Parallel lines Current thread, executed one by one Open a bunch of threads, execute them together

Create a queue

  • Main queue: This is a special serial queue. The main queue, as you all know, is used to refresh the UI, and anything that needs to refresh the UI is done in the main queue, so time-consuming tasks are usually done in another thread.

      //OBJECTIVE-C
      dispatch_queue_t queue = ispatch_get_main_queue();
    
      //SWIFT
      let queue = ispatch_get_main_queue()Copy the code
  • Self-created queues: All self-created queues are serial queues. The first parameter is an identifier, which identifies a unique queue for debugging and can be null. You can look at the documentation in Xcode and see what the parameters mean.

    Update: You can create serial queues or parallel queues yourself. Look at the code below (the code has been updated). It takes two arguments, the first of which is stated above, and the second is the most important. The second parameter is used to indicate whether the queue created is serial or parallel, passing DISPATCH_QUEUE_SERIAL or NULL to indicate serial queue creation. Pass DISPATCH_QUEUE_CONCURRENT to create a parallel queue.

    // objective-c // dispatch_queue_t queue = dispatch_queue_create("tk.bourne. TestQueue ", NULL); dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL); // dispatch_queue_t queue = dispatch_queue_create("tk.bourne. TestQueue ", DISPATCH_QUEUE_CONCURRENT); //SWIFT // serial queue let queue = dispatch_queue_create("tk.bourne. TestQueue ", nil); let queue = dispatch_queue_create("tk.bourne.testQueue", Let queue = dispatch_queue_create("tk.bourne. TestQueue ", DISPATCH_QUEUE_CONCURRENT)Copy the code
  • Global parallel queue: This should be the only parallel queue to which all parallel tasks are generally added. This is a concurrent queue provided by the system.

      //OBJECTIVE-C
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
      //SWIFT
      let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)Copy the code

Create a task

  • SYNC task: does not open another thread;

    OBJECTIVE-C
      dispatch_sync(<#queue#>, ^{
          //code here
          NSLog(@"%@", [NSThread currentThread]);
      });Copy the code
    SWIFT
      dispatch_sync(<#queue#>, { () -> Void in
          //code here
          println(NSThread.currentThread())
      })Copy the code
  • ASYNC: does not block the current thread

    OBJECTIVE-C
      dispatch_async(<#queue#>, ^{
          //code here
          NSLog(@"%@", [NSThread currentThread]);
      });Copy the code
    SWIFT
      dispatch_async(<#queue#>, { () -> Void in
          //code here
          println(NSThread.currentThread())
      })Copy the code

Update: To better understand synchronous and asynchronous, and the use of various queues, let’s look at two examples:

Example 1: What is the result of the following code being called on the main thread?

NSLog(" before - %@", nsthread.currentThread ()) dispatch_sync(dispatch_get_main_queue(), {() -> Void in NSLog(" before - %@", nsthread.currentThread ()) dispatch_sync(dispatch_get_main_queue(), {() -> Void in NSLog(" after - %@", Nsthread.currentthread ())}) NSLog(" after - %@", nsthread.currentThread ())Copy the code

– {number = 1, name = main} – {number = 1, name = main} – {number = 1, name = main} – {number = 1, name = main} – {number = 1, name = main} – {number = 1, name = main} – {number = 1, name = main} Explanation: A synchronization task blocks the current thread, and then puts the tasks in the Block in the specified queue for execution. The current thread is not allowed to continue until the tasks in the Block are complete. So the steps here are: After printing the first sentence, dispatch_sync immediately blocks the main thread and puts the tasks in the Block to the main_queue. The tasks in the main_queue can be taken out and put into the main thread, but the main thread is already blocked. So the task in the Block won’t complete, and until it does, dispatch_sync will Block the main thread, which is a deadlock. The main thread is stuck.

Example 2: What results from the following code?

Let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL) NSLog(" before - %@", Nsthread.currentthread ()) dispatch_async(queue, {() -> Void in NSLog(" before sync - %@", NSThread.currentThread()) dispatch_sync(queue, { () -> Void in NSLog("sync - %@", Nsthread.currentthread ())}) NSLog(" after sync - %@", nsthread.currentThread ())}) NSThread.currentThread())Copy the code

The answer: 2015-07-30 02:06:51.058 test[33329:8793087] – {number = 1, Name = main} 2015-07-30 02:06:51.059 test[33329:8793356] sync before – {number = 2, Name = (null)} 2015-07-30 02:06:51.059 test[33329:8793087] – {number = 1, Name = main} sync – %@ and sync – %@ are not printed. Why is that? Let’s analyze it again:

Analysis: Let’s go step by step in order of execution:

  1. useDISPATCH_QUEUE_SERIALThis parameter creates oneSerial queues.
  2. Print out the- % @ beforeThis sentence.
  3. dispatch_asyncAsynchronously, so the current thread doesn’t block, so you have two threads, and one current thread prints down- % @The other one prints the contents of the BlockBefore sync - %@This sentence. Since the two are running in parallel, it doesn’t matter what order you print in.
  4. Watch out, the orgasm is coming. Now it’s the same as the last example.dispatch_syncSynchronous execution, and the thread that it’s in will block untilsyncI won’t move on until I finish my mission. sosyncI’m happy to put tasks in my BlockqueueYes, but who wants toqueueIt’s a serial queue, one task at a time, sosyncThe Block must wait until the previous task completes, which is unexpectedqueueThe task being performed is to besyncThe one that’s blocked. So there’s another deadlock. sosyncThe thread is stuck. The remaining two lines of code will not print.

Queue group

A queue group can add many queues to a group. The advantage of this is that the queue group has a method to notify us when all the tasks in the group have finished. Here is how to use, this is a very practical function.

OBJECTIVE-C
//1. Create queue group dispatch_group_t group = dispatch_group_create(); Dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //3. Execute tasks using queue group methods multiple times, only asynchronous method //3.1. Execute 3 cycles dispatch_group_async(group, queue, ^{for (NSInteger I = 0; i < 3; i++) { NSLog(@"group-01 - %@", [NSThread currentThread]); }}); Dispatch_group_async (group, dispatch_get_main_queue(), ^{for (NSInteger I = 0; i < 8; i++) { NSLog(@"group-02 - %@", [NSThread currentThread]); }}); Execute 5 cycles dispatch_group_async(group, queue, ^{for (NSInteger I = 0; i < 5; i++) { NSLog(@"group-03 - %@", [NSThread currentThread]); }}); ^{NSLog(@" done - %@", [NSThread currentThread]); });Copy the code
SWIFT
Let group = dispatch_group_create() //2. Let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //3 Execute 3 times loop dispatch_group_async(group, queue) {() -> Void in for _ in 0.. < 3 {= "" NSLog (" group - 01 =" "- =" % @ ", "=" "NSThread currentThread ()) =" "} = "3.2". Execute 8 cycles for main queue ="" dispatch_group_async(group,="" dispatch_get_main_queue())="" ()=""> Void in for _ in 0.. 8 {< = "" NSLog (" group - 02 =" "- =" % @ ", "=" "NSThread currentThread ()) =" "} = "3.3". Execute 5 cycles ="" dispatch_group_async(group,="" queue)="" ()=""> Void in for _ in 0.. <5 {="" NSLog("group-03="" -="" %@",="" NSThread.currentThread())="" }="" 4. ="" dispatch_group_notify(group,="" dispatch_get_main_queue())="" ()=""> Void in NSLog(" done - %@", NSThread.currentThread()) }Copy the code

Print the result

2015-07-28 03:40:34.277 test[12540:3319271] group-03 – {number = 3, name = (null)}

2015-07-28 03:40:34.277 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319271] group-03 – {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 – {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 – {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.277 test[12540:3319273] group-01 – {number = 2, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319271] group-03 – {number = 3, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319273] group-01 – {number = 2, name = (null)}

2015-07-28 03:40:34.278 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.278 test[12540:3319273] group-01 – {number = 2, name = (null)}

2015-07-28 03:40:34.279 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.279 test[12540:3319146] group-02 – {number = 1, name = main}

2015-07-28 03:40:34.279 test[12540:3319146] complete – {number = 1, name = main}

These are the basic features of GCD, but it’s capable of much more than that. After we cover NSOperation, we’ll look at some of its other uses. And, as long as you have a good imagination, you can come up with better uses.

Update: Two more things to say about GCD:

  • func dispatch_barrier_async(_ queue: dispatch_queue_t, _ block: dispatch_block_t): This method focuses on the queue you pass in, and blocks the queue (not the current thread) when the queue you pass in is a self-created queue with the DISPATCH_QUEUE_CONCURRENT parameter. It waits until all the tasks ahead of it in the queue have finished before it starts executing itself, and then it unblocks, allowing the tasks behind it in the queue to continue executing. If you’re passing in some other queue, it’s the same as dispatch_async.

  • func dispatch_barrier_sync(_ queue: dispatch_queue_t, _ block: dispatch_block_t): This method is used as before, passing in a custom concurrent queue (DISPATCH_QUEUE_CONCURRENT), which blocks the same queue as before, except that this method also blocks the current thread. If you pass in another queue, it will be the same as dispatch_sync.

NSOperation and NSOperationQueue

NSOperation is apple’s version of GCD, completely object-oriented, so it’s easier to understand. You can see that NSOperation and NSOperationQueue correspond to GCD tasks and queues respectively. The steps are also easy to understand:

  1. The tasks to be performed are encapsulated into oneNSOperationIn the object.
  2. Add this task to aNSOperationQueueIn the object.

Then the system will automatically perform the task. For synchronous or asynchronous, serial or parallel, read on:

Add tasks

It’s worth noting that NSOperation is just an abstract class, so it doesn’t encapsulate tasks. But it has two subclasses for encapsulating tasks. The values are NSInvocationOperation and NSBlockOperation. After an Operation is created, the start method is called to start the task, which is executed synchronously on the current queue by default. Of course, you can also cancel a task in mid-stream by calling its Cancel method.

  • NSInvocationOperation: You need to pass in a method name.

    OBJECTIVE-C
    NSInvocationOperation * Operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //2. Start [operation start];Copy the code
    SWIFT

    In the harmonious society constructed by Swift, there is no room for such scum as NSInvocationOperation that is not type safe. “Apple said. Here’s the explanation

  • NSBlockOperation

    OBJECTIVE-C
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"%@", [NSThread currentThread]); }]; //2. Start task [operation start];Copy the code
    SWIFT
    Let operation = NSBlockOperation {() -> Void in println(nsThread.currentThread ())} //2. Start task operation.start()Copy the code

    As mentioned earlier, such tasks are executed on the current thread by default. But NSBlockOperation also has a method: addExecutionBlock, which adds multiple execution blocks to the Operation. The tasks in Operation are executed concurrently. It will execute these tasks on the main thread and on multiple threads. Note the following print:

    OBJECTIVE-C
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"%@", [NSThread currentThread]); }]; // Add multiple blocks for (NSInteger I = 0; i < 5; I ++) {[operation addExecutionBlock:^{NSLog(@" %ld time: %@", I, [NSThread currentThread]);}]; } //2. Start task [operation start];Copy the code
    SWIFT
    Let operation = NSBlockOperation {() -> Void in NSLog("%@", nsThread.currentThread ())} //2. Add multiple blocks for I in 0.. 5 {< = "" operation. AddExecutionBlock = () =" "-" "=" "> Void in NSLog (" ld times - the first % % @", I, NSThread. CurrentThread ())}} / / 2. Start task operation.start()Copy the code
    A printout

    2015-07-28 17:50:16.585 test[17527:4095467] 第 二 次 – {number = 1, name = main}

    2015-07-28 17:50:16.585 test[17527:4095666] 第1次 – {number = 4, name = (null)}

    2015-07-28 17:50:16.585 test[17527:4095665] {number = 3, name = (null)}

    2015-07-28 17:50:16.585 test[17527:4095662] 第0 – {number = 2, name = (null)}

    2015-07-28 17:50:16.586 test[17527:4095666] 第 二 次 – {number = 4, name = (null)}

    2015-07-28 17:50:16.586 test[17527:4095467] 第4 行 – {number = 1, name = main}

    NOTE: The addExecutionBlock method must be executed before the start() method or an error will be reported:

    ‘* * * – [NSBlockOperation addExecutionBlock:] : blocks cannot be added after the operation has started executing or finished’

    NOTE: You may have spotted a problem. Why do I use NSLog() instead of println() in Swift? The reason is that with print()/println(), it simply uses the concept of stream, as anyone who has studied C++ knows. It prints each character to the console one by one. This is not a problem in general use, but when multiple threads are printed simultaneously, the characters on the console are jumbled together because many println() are printed at the same time. NSLog() does not have this problem. What does it look like? You could just change the NSLog of alpha to println of alpha, and try it out. See more of the differences between NSLog() and println() here

  • The custom Operation

    In addition to the two operations above, we can also customize operations. To customize Operation, you need to inherit the NSOperation class and implement its main() method, because the main() method is called internally to complete the logic when the start() method is called. So if the above two classes do not satisfy your desire, you need to customize. Whatever function you want to implement can be written in it. In addition, you need to implement various methods including cancel(). So this feature is for advanced players, I won’t talk about it here, I’ll study it when I need it, and I’ll probably update it later.

Create a queue

As you can see from the above, we can call the start() method of an NSOperation object to start the task, but they are executed synchronously by default. Even if the addExecutionBlock method is executed in the current thread and in other threads, it will still occupy the current thread. And that’s where the NSOperationQueue comes in. Moreover, there are two types in terms of type: main queue and other queue. As soon as it is added to the queue, the start() method of the task is automatically called

  • The home side column

    If you are careful, you will notice that each multi-threaded scheme has a main thread (of course, in iOS, multi-system schemes such as pthreads do not have one, because UI threading theory requires each operating system to customize it). This is a special thread that must be serialized. So tasks added to the main queue are queued up for processing on the main thread, one after another.

    //OBJECTIVE-C
    NSOperationQueue *queue = [NSOperationQueue mainQueue];
    
    //SWIFT
    let queue = NSOperationQueue.mainQueue()Copy the code
  • Other queue

    Because the main queue is special, there is a separate class method to get the main queue. Then the queue generated by initialization is the other queue, because these are the only two queues that do not need names except for the main queue.

    Note: Tasks on other queues are executed in parallel on other threads.

    OBJECTIVE-C
    //1. Create another queue NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"%@", [NSThread currentThread]); }]; // add multiple blocks for (NSInteger I = 0; i < 5; I ++) {[operation addExecutionBlock:^{NSLog(@" %ld time: %@", I, [NSThread currentThread]);}]; } //4. Queue addOperation:operation;Copy the code
    SWIFT
    Create another queue let queue = NSOperationQueue() //2. Let operation = NSBlockOperation {() -> Void in NSLog("%@", nsThread.currentThread ())} //3 Add multiple blocks for I in 0.. 5 {< = "" operation. AddExecutionBlock = () =" "-" "=" "> Void in NSLog (" ld times - the first % % @", I, NSThread. CurrentThread ())}} / / 4. Queue adding task queue.addOperation(operation)Copy the code
    A printout

    2015-07-28 20:26:28.463 test[18622:4443534] {number = 5, name = (null)}

    2015-07-28 20:26:28.463 test[18622:4443536] 第2次 – {number = 2, name = (null)}

    2015-07-28 20:26:28.463 test[18620:4443535] 第0 – {number = 4, name = (null)}

    2015-07-28 20:26:28.463 test[18622:4443533] 第1次 – {number = 3, name = (null)}

    2015-07-28 20:26:28.463 test[18620:4443534] 第3 行 – {number = 5, name = (null)}

    2015-07-28 20:26:28.463 test[18620:4443536] 第4 行 – {number = 2, name = (null)}

OK, now it’s time to ask, if you compare NSOperationQueue to the GCD queue, there’s no parallel queue, so what if I want 10 tasks to be executed sequentially on another thread?

That’s the beauty of apple packaging. You don’t care about serial, parallel, synchronous, asynchronous. NSOperationQueue maxConcurrentOperationCount maximum concurrency, a parameter is used to set up to make how many tasks to perform at the same time. When you set it to 1, it is serial.

NSOperationQueue also has a way to add a task, – (void)addOperationWithBlock:(void (^)(void))block; Is it similar to the COMMUNIST Party of China? This makes it very convenient to add a task to the queue.

NSOperation has a very useful function, which is to add dependencies. For example, there are three tasks: A: download an image from the server, B: add A watermark to the image, and C: return the image to the server. This is where dependencies come in:

OBJECTIVE-C
//1. Task 1: NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" download picture - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; //2. Task 2: NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" watermark - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; //3. Task 3: NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" upload image - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; //4. Set dependency [operation2 addDependency:operation1]; // Task two depends on task one [operation3 addDependency:operation2]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];Copy the code
SWIFT
//1. Task 1: Let operation1 = NSBlockOperation {() -> Void in NSLog(" - %@", NSThread. CurrentThread ()) NSThread. SleepForTimeInterval} / / 2 (1.0). Task 2: Let operation2 = NSBlockOperation {() -> Void in NSLog(" %@", NSThread. CurrentThread ()) NSThread. SleepForTimeInterval (1.0)} / / 3. Task three: Let operation3 = NSBlockOperation {() -> Void in NSLog(" - %@", NSThread. CurrentThread ()) NSThread. SleepForTimeInterval (1.0)} / / 4. Set dependency operation2.addDependency(operation1) // Task two depends on task one operation3.addDependency(operation2) // Task three depends on task two //5. Let Queue = NSOperationQueue() queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)Copy the code
Print the result

Test [19392:4637517] 下载图 – {number = 2, name = (null)}

Test [19392:4637515] – {number = 3, name = (null)}

Test [19392:4637515] – {number = 3, name = (null)}

  • Note: You cannot add interdependencies because they deadlock, such as A dependent on B, B dependent on A.
  • You can useremoveDependencyTo release the dependency.
  • You can depend on different queues, but the dependency is added to the task, regardless of the queue.

Other methods

These are some of the main methods, but here are some common ones to be aware of:

  • NSOperation

    BOOL executing; // Check whether the task is being executed

    BOOL finished; // Check whether the task is complete

    void (^completionBlock)(void); // Set the action to be performed after completion

    – (void)cancel; // Cancel the task

    – (void)waitUntilFinished; // Block the current thread until the task completes

  • NSOperationQueue

    NSUInteger operationCount; // Get the number of tasks in the queue

    – (void)cancelAllOperations; // Cancel all tasks in the queue

    – (void)waitUntilAllOperationsAreFinished; // Block the current thread until all tasks in this queue are completed

    [queue setSuspended:YES]; / / suspend the queue

    [queue setSuspended:NO]; / / continue to queue

Well, that’s pretty much it. Of course, I’m not going to cover all of this, and there may be some things that I haven’t covered, but as a general method, that’s enough. I’m just showing you some of the methods here, but it takes a lot of practice to apply them to the right places. I’m going to talk a little bit about multithreading, just to give you a little bit more insight.

Other USES

In this section, I will talk about some cases related to multithreading knowledge, perhaps some very simple, we already know, but because this article is about multithreading, so it should be as comprehensive as possible. And I’m going to do it as many ways as possible, just to show you the difference.

Thread synchronization

Thread synchronization is a measure taken to prevent data security problems caused by multiple threads grabbing the same resource. Of course, there are also many implementation methods, please read down:

  • Mutex: Adding a mutex to a block of code that needs to be synchronized ensures that only one thread accesses the block at a time.

    OBJECTIVE-C
    @synchronized(self) {// synchronized(self)Copy the code
    SWIFT
    Objc_sync_enter (self) // Code block to execute objc_sync_exit(self)Copy the code
  • Synchronous execution: We can use our knowledge of multithreading to implement the concept of thread synchronization by adding this code to the same serial queue for multiple threads to execute. Of course you can use both GCD and NSOperation, so LET me write them out.

    OBJECTIVE-C
Dispatch_sync (queue, ^{NSInteger ticket = lastTicket; //GCD // dispatch_sync(queue, ^{NSInteger ticket = lastTicket; [NSThread sleepForTimeInterval: 0.1]; NSLog(@"%ld - %@",ticket, [NSThread currentThread]); ticket -= 1; lastTicket = ticket; }); //NSOperation & NSOperationQueue Global NSOperationQueue, all operations added to the same queue // 2. Set the queue maxConcurrentOperationCount to 1/3. If subsequent operations need the results in the Block, you need to call waitUntilFinished for each operation, blocking the current thread until the current operation is complete before allowing subsequent operations to execute. WaitUntilFinished is added to the queue! NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSInteger ticket = lastTicket;  [NSThread sleepForTimeInterval:1]; NSLog(@"%ld - %@",ticket, [NSThread currentThread]); ticket -= 1;  lastTicket = ticket; }]; [queue addOperation:operation]; [operation waitUntilFinished]; // What to do nextCopy the code
SWIFT

Here swift code, I will not write, because each sentence is the same, but the syntax is different, according to OC code can write swift. This article is so long that I won’t waste any space. It’s not a high school essay.

Delay the

Delayed execution is a delay in executing a piece of code. Here are some common methods.

  • perform

    OBJECTIVE-C
    // call self's run: method after 3 seconds, passing @" ABC "[self performSelector:@selector(run: withObject:@" ABC" afterDelay:3];Copy the code
    SWIFT
    As I said before, this method is removed from Swift.Copy the code
  • GCD

    You can use the dispatch_after method in GCD, OC and Swift can be used, here only OC, Swift is the same.

    OBJECTIVE-C
    Dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); Double delay = 3; Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{// Task to be performed after 3 seconds});Copy the code
  • NSTimer

    NSTimer is a timer class in iOS that can be used for more than just deferred execution. Once again, just write the OC version, Swift is the same thing.

    OBJECTIVE-C
    [NSTimer scheduledTimerWithTimeInterval: 3.0 target: self selector: @ the selector (run) the userInfo: @ "ABC" repeats: NO];Copy the code

The singleton pattern

I’m not going to say much about what a singleton is, just how it’s implemented in general. In Objective-C, the way to implement singletons is pretty specific, there are other ways, but it’s generally a standard way, so let’s see.

OBJECTIVE-C
@interface Tool : NSObject 

+ (instancetype)sharedTool;

@end

@implementation Tool

static id _instance;

+ (instancetype)sharedTool {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[Tool alloc] init];
    });

    return _instance;
}

@endCopy the code

The singleton mode is used here because GCD’s dispatch_once method is used. Let’s look at the singleton pattern in Swift. In Swift, the singleton pattern is very simple! Want to know how to go from OC to the following, see here

SWIFT
Class Tool: NSObject {static let sharedTool = Tool() private override init() {}}Copy the code

A method to return to the main thread from another thread

We all know that we have to go to the main thread to update the UI after the other thread has finished. So, with all the multithreading scenarios covered, let’s look at some ways to get back to the main thread.

  • NSThread

    //Objective-C [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO]; //Swift //Swift cancels performSelector.Copy the code
  • GCD

    //Objective-C
    dispatch_async(dispatch_get_main_queue(), ^{
    
    });
    
    //Swift
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
    
    })Copy the code
  • NSOperationQueue

    //Objective-C
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    
    }];
    
    //Swift
    NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in
    
    }Copy the code

conclusion

Ok, finally finished, pure hand knock more than 6K words, moved me to death. It took two days, and the time span is a bit long, so maybe some parts are not followed or incomplete. If you look hard or have any problems, please let me know in the comments section, AND I will revise it in time. Of course, there’s more to multithreading than that, and the topic is just a topic, don’t take it seriously. If you want to know more, you have to dig up relevant information on the Internet yourself. Read the official documentation. I can’t make it up anymore. By the way, seeing how hard I’ve worked, it would be nice if I liked it if I didn’t have to.

Update: there are many mistakes in the first release, thank you very much. If you see something wrong, point it out. It helps everyone. There is also a point for beginners, encounter do not understand the method, the best way is to check the official document, there is the most accurate, even if there are a few words do not know, check it, will not affect the overall understanding. I saw a website republish my article, but there may be some problems with the reprint, and I can only update it in the simple book, so if you want to see the full version, go to the simple book: here is the address.