IOS underlying principles + reverse article summary

The main purpose of this article is to introduce the common usage of NSThread, GCD, and NSOperation

NSthread

NSthread is an object oriented thread operation technology officially provided by Apple. It is the upper layer encapsulation of Thread and prefers the lower layer. Simple and convenient, can directly operate the thread object, less frequent use.

Creating a thread A thread can be created in the following three ways

  • Created using init initialization

  • Created by the detachNewThreadSelector constructor

  • Through the performSelector… Method to retrieve the main thread, as well as background threads

//1, create - (void)cjl_createNSThread{NSString *threadName1 = @nsthread1; NSString *threadName2 = @"NSThread2"; NSString *threadName3 = @"NSThread3"; NSString *threadNameMain = @"NSThreadMain"; // Mode 1: Initialization mode, NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething: object:threadName1); [thread1 start]; [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:threadName2]; // Selector... Method to create [self performSelectorInBackground: @ the selector (doSomething:) withObject: threadName3]; / / way four [self performSelectorOnMainThread: @ the selector (doSomething:) withObject: threadNameMain waitUntilDone: YES]; } - (void)doSomething:(NSObject *)objc{ NSLog(@"%@ - %@", objc, [NSThread currentThread]); }Copy the code

attribute

IsCancelled // the thread isCancelled. -thread. isFinished // whether it isFinished. -thread. isMainThread // whether it is the main thread - thread.threadPriority // Specifies the priority of the thread. The value ranges from 0.0 to 1.0Copy the code

Class methods There are several common class methods:

  • CurrentThread: gets the currentThread

  • sleep… : Block thread

  • Exit: exits the thread

  • MainThread: obtains the mainThread

- (void)cjl_NSThreadClassMethod{// currentThread [NSThread currentThread]; NSLog(@"%@", [NSThread currentThread]); NSLog(@"%@", [NSThread currentThread]); // Block sleep [NSThread sleepForTimeInterval:2]; [NSThread sleepUntilDate:[NSDate date]]; [NSThread exit]; // Exit the thread [NSThread isMainThread]; // Determine whether the current thread is the master thread [NSThread isMultiThreaded]; NSThread *mainThread = [NSThread mainThread]; NSLog(@"%@", mainThread);Copy the code

GCD

dispatch_after

- (void)cjl_testAfter{/* dispatch_after indicates delayed execution of blocks in a queue. Delay the execution of a task on the main queue, such as viewDidload for 1 second after the delay, prompting an alertView */ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@" output after 2s "); }); }Copy the code

dispatch_once

- (void)cjl_testOnce{/* dispatch_once guarantees that the code in the block is executed only once during the App run */ static dispatch_once_t onceToken; Dispatch_once (&onceToken, ^{// create singleton, method swizzled, or other task NSLog(@" create singleton "); }); }Copy the code

dispatch_apply

- (void)cjl_testApply{/* dispatch_apply appends the specified Block to the specified queue and repeats execution until all processing is completed -- equivalent to a thread-safe for loop application scenario: It is used to calculate the size of each control in advance after pulling network data to prevent calculation during drawing. Improve form sliding fluency - Add to serial queue - Execute in order - Add to main queue - Deadlock - Add to concurrent queue - Execute out of order - Add to global queue - Execute out of order */ dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL); NSLog (@ "before dispatch_apply"); /** param1: indicates the number of repetitions. Param2: indicates the appended queue. Param3: indicates the appended queue. */ dispatch_apply(10, queue, ^(size_t index) {NSLog(@"dispatch_apply thread %zu - %@", index, [NSThread currentThread]); }); NSLog (@ "after dispatch_apply"); }Copy the code

dispatch_group_t

There are two ways to use it

  • 【 Method 1 】 Usedispatch_group_async + dispatch_group_notify
- (void)cjl_testGroup1{/* dispatch_group_t: Dispatch_group_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t: dispatch_t Refresh page after multiple interface requests */ dispatch_GROUP_t group = dispatch_group_CREATE (); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); Dispatch_group_async (group, queue, ^{NSLog(@" request completed "); }); Dispatch_group_async (group, queue, ^{NSLog(@" request 2 completed "); }); Dispatch_group_notify (group, dispatch_get_main_queue(), ^{NSLog(@" refresh page "); }); }Copy the code
  • 【 Method 2 】 Usedispatch_group_enter + dispatch_group_leave + dispatch_group_notify
- (void)cjl_testGroup2{/* dispatch_group_enter and dispatch_group_leave are paired, */ dispatch_group_t group = dispatch_group_CREATE (); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request completed "); dispatch_group_leave(group); }); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request 2 completed "); dispatch_group_leave(group); }); Dispatch_group_notify (group, dispatch_get_main_queue(), ^{NSLog(@" refresh page "); }); }Copy the code
  • Add timeout to method 2dispatch_group_wait
- (void)cjl_testGroup3{/* long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout) group: Timeout: The timeout period for waiting (i.e. how long) - for DISPATCH_TIME_NOW means that you do not wait to determine whether the dispatch group has completed its execution. - for DISPATCH_TIME_FOREVER, it blocks the current dispatch group until the dispatch group has completed its execution. If the value is long - The return value is 0 -- the dispatch group completed the task within the specified period of time - the return value is not 0 -- the dispatch group did not complete the task within the specified period of time */ dispatch_group_t group = dispatch_group_CREATE (); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request completed "); dispatch_group_leave(group); }); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request 2 completed "); dispatch_group_leave(group); }); // long timeout = dispatch_group_wait(group, DISPATCH_TIME_NOW); // long timeout = dispatch_group_wait(group, DISPATCH_TIME_FOREVER); long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 *NSEC_PER_SEC)); NSLog(@"timeout = %ld", timeout); If (timeout == 0) {NSLog(@" finish the task on time "); }else{NSLog(@" timeout "); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@" refresh "); }); }Copy the code

dispatch_barrier_sync & dispatch_barrier_async

The fence function can be used in two main scenarios: serial queue and concurrent queue

- (void)cjl_testBarrier{/* dispatch_barrier_sync & dispatch_barrier_async Application scenario: After the tasks that are added to the queue in front of the barrier, such as synchronization locks, are completed, the tasks behind the barrier are added to the queue. In short, do the pre-fence task, then the fence task, and then the post-fence task - dispatch_barrier_async: This is where the previous task will be executed - dispatch_barrier_sync: - Dispatch_barrier_Async controls the order in which tasks in the queue are executed. - Dispatch_barrier_sync not only blocks the queue, */ [self cjl_testBarrier1]; [self cjl_testBarrier2]; } - (void)cjl_testBarrier1{dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL); NSLog(@" start - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" 1 - %@", [NSThread currentThread]); }); NSLog(@" end of first time - %@", [NSThread currentThread]); // The fence function is used to group tasks in a queue, So we just focus on task 1 and task 2 dispatch_barrier_async (queue, ^ {NSLog (@ "-- -- -- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- -- - % @", [NSThread currentThread]); }); NSLog(@" end of fence - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" 2 - %@", [NSThread currentThread]); }); NSLog(@" end of second time - %@", [NSThread currentThread]); } - (void)cjl_testBarrier2{dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT); NSLog(@" start - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" 1 - %@", [NSThread currentThread]); }); NSLog(@" end of first time - %@", [NSThread currentThread]); // Since the concurrent queue asynchronously executes tasks out of order, So use the fence function can be very good control over the order of tasks within the queue dispatch_barrier_async (queue, ^ {NSLog (@ "-- -- -- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- -- -- -- % @". [NSThread currentThread]); }); NSLog(@" end of fence - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" 2 - %@", [NSThread currentThread]); }); NSLog(@" end of second time - %@", [NSThread currentThread]); }Copy the code

dispatch_semaphore_t

The semaphore is mainly used as a synchronization lock to control the maximum number of concurrent GCD

- (void)cjl_testSemaphore{/* Application scenario: Synchronize the lock to control the maximum number of concurrent GCD requests. - dispatch_semaphore_create() : create a semaphore. - dispatch_semaphore_wait() : Wait for the semaphore. Subtract 1 from the semaphore. When the semaphore is less than zero, the current thread is blocked, and the next action is determined based on the incoming wait time -- if the wait is permanent, the next action will be executed until the signal. -dispatch_semaphore_signal () : The semaphore is released and the semaphore is increased by one. */ dispatch_queue_t queue = dispatch_queuE_CREATE ("CJL", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; I ++) {dispatch_async(queue, ^{NSLog(@" current - %d, thread - %@", I, [NSThread currentThread]); }); } sem = dispatch_semaphore_create(0); for (int i = 0; i < 10; I ++) {dispatch_async(queue, ^{NSLog(@" current - %d, thread - %@", I, [NSThread currentThread]); dispatch_semaphore_signal(sem); }); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); }}Copy the code

dispatch_source_t

Dispatch_source_t is primarily used for timing operations because it creates a timer that does not depend on RunLoop and has a higher timing accuracy than NSTimer

- (void)cjl_testSource{/* dispatch_source Application scenarios: GCDTimer generally uses NSTimer in iOS development to handle timing logic, but NSTimer relies on Runloop, which can run in different modes. If the NSTimer is added in one mode, the timer will hang up when the Runloop runs in other modes. If the Runloop is blocked, the NSTimer firing time is delayed until the next Runloop cycle. The GCD Timer does not rely on Runloop and is much more accurate. Dispatch_source is a basic data type that can be used to listen for low-level system events. - Timer Dispatch Source: Timer event Source, used to generate periodic notifications or callbacks. - Signal Dispatch Source: Listener event Source, which notifies when a UNIX Signal occurs. - Descriptor Dispatch Source: - Process Dispatch Source: Listens to Process event sources and notifies process-related events - Mach Port Dispatch Source: listens to Process event sources and notifies process-related events: - Custom Dispatch Source: Listens to Custom event sources. The following apis are used for listening to Custom event sources: -dispatch_source_create: Create event source - dispatch_source_set_event_handler: Set data source call-dispatch_source_merge_data: - dispatch_source_get_data: Obtain event source data. - dispatch_resume: continue. - dispatch_suspend: suspend. Dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //2. Create timer dispatch_source_t timer = dispatch_sourCE_CREATE (DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); Set the first execution time, interval, and accuracy of the timer. Dispatch_source_set_timer (timer, DISPATCH_TIME_NOW, 2.0*NSEC_PER_SEC, 0.1*NSEC_PER_SEC). //4. Set timer event callback dispatch_source_set_event_handler(timer, ^{NSLog(@"GCDTimer"); }); //5. The status of dispatch_resume(timer) is suspended by default. You need to manually activate dispatch_resume(timer). }Copy the code

NSOperation

NSOperation is a higher level of encapsulation based on GCD. NSOperation needs to cooperate with NSOperationQueue to realize multi-threading.

NSOperatino implements multithreading as follows:

  • 1. Create a task: Encapsulate the operations to be performed into NSOperation objects.

  • 2. Create queue: Create NSOperationQueue.

  • 3. Add the task to the queue: Add the NSOperation object to the NSOperationQueue.

NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation::) object:@"CJL"]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Create a queue. [queue addOperation:op]; } - (void)handleInvocation:(id)operation{ NSLog(@"%@ - %@", operation, [NSThread currentThread]); }Copy the code

It’s important to note that NSOperation is an abstract class, so there are three ways to use a subclass of NSOperation:

  • 1. Use subclassesNSInvocationOperation
// Handle transactions directly without adding hidden queues - (void)cjl_createNSOperation{// Create NSInvocationOperation object and associate method, then start. NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"CJL"]; [invocationOperation start]; }Copy the code
  • 2. Use subclassesNSBlockOperation
- (void) cjl_testNSBlockOperationExecution {/ / by addExecutionBlock this method could make NSBlockOperation realize multithreading. // When NSBlockOperation is created, tasks in the block are executed on the main thread, while tasks added using addExecutionBlock are executed on the child thread. NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"main task = >currentThread: %@", [NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ NSLog(@"task1 = >currentThread: %@", [NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ NSLog(@"task2 = >currentThread: %@", [NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ NSLog(@"task3 = >currentThread: %@", [NSThread currentThread]); }]; [blockOperation start]; }Copy the code
  • 3. Definitions inherit fromA subclass of NSOperation, by implementing internal methods to encapsulate tasks.
/ / * * * * * * * * * custom inherited from NSOperation subclasses of * * * * * * * * * @ interface CJLOperation: NSOperation @end @implementation CJLOperation - (void)main{ for (int i = 0; i < 3; I++) {NSLog(@"NSOperation subclass: %@",[NSThread currentThread]); }} @end //********* use ********* - (void)cjl_testCJLOperation{// use a subclass that extends from NSOperation. Then override its main method. CJLOperation *operation = [[CJLOperation alloc] init]; [operation start]; }Copy the code

NSOperationQueue

NSOperationQueue adds a transaction

NSOperationQueue has two types of queues: main queue and other queues. Other queues include serial and concurrent.

  • Main queue: Tasks on the main queue are executed on the main thread.

  • Other queues (non-main queues) : Tasks added to ‘non-queues’ are concurrent by default, enabling multi-threading.

- (void)cjl_testNSOperationQueue{/* NSInvocationOperation and NSBlockOperation are different: The NSOperationQueue is executed asynchronously. The NSOperationQueue is executed asynchronously. The NSOperationQueue is executed asynchronously. NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{NSBlockOperation blockOperationWithBlock:^{ NSLog (@ "task 1 -- - % @", [NSThread currentThread]);}]; // Add transaction [bo addExecutionBlock:^{NSLog(@" task 2————%@",[NSThread currentThread]);}]; // Callback listener bo.completionBlock = ^{NSLog(@" done!!!") ); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo]; NSLog(@" transaction added to NSOperationQueue"); }Copy the code

Set the execution sequence

// Execution order - (void)cjl_testQueueSequence{NSOperationQueue *queue = [[NSOperationQueue alloc] init]; for (int i = 0; i < 5; i++) { [queue addOperationWithBlock:^{ NSLog(@"%@---%d", [NSThread currentThread], i); }]; }}Copy the code

Setting priority

- (void)cjl_testOperationQuality{/* NSOperation setting priority will only make the CPU more likely to call, This is not to say that setting high is always done first - don't use sleep - high priority task 1 precedes low priority task 2 - Use sleep for latency - high priority task 1 is slower than low priority task 2 */ NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{for (int I = 0; I < 5; I ++) {//sleep(1); NSLog(@" first operation %d -- %@", I, [NSThread currentThread]); } }]; / / set the highest priority bo1. QualityOfService = NSQualityOfServiceUserInteractive; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{for (int I = 0; I < 5; I ++) {NSLog(@" second operation %d -- %@", i, [NSThread currentThread]); } }]; / / set the lowest priority bo2. QualityOfService = NSQualityOfServiceBackground; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo1]; [queue addOperation:bo2]; }Copy the code

Setting concurrency

// Set concurrency - (void)cjl_testOperationMaxCount{/* In GCD, only semaphores can be used to set concurrency. NSOperation can easily set concurrency By setting the maxConcurrentOperationCount to control a single queue to execute the task queue number * / NSOperationQueue * = [[NSOperationQueue alloc] init]; queue.name = @"Felix"; queue.maxConcurrentOperationCount = 2; for (int i = 0; i < 5; I ++) {[queue addOperationWithBlock:^{// a task [NSThread sleepForTimeInterval:2]; NSLog(@"%d-%@", I,[NSThread currentThread]); }]; }}Copy the code

Add the dependent

NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Add a dependency - (void)cjl_testOperationDependency{NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@" request token");}]; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@" hold token, request data 1");}]; NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:0.5]; NSLog(@" hold data 1, request data 2");}]; [bo2 addDependency:bo1]; [bo3 addDependency:bo2]; [queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES]; NSLog at sign "is that done? I'm going to do something else "); }Copy the code

Interthread communication

// Inter-thread communication - (void)cjl_testOperationNoti{NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.name = @"Felix"; [queue addOperationWithBlock:^{NSLog(@" request network %@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); [[NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@" refresh UI%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); }]; }]; }Copy the code