Summary of basic principles of iOS

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

NSthread

NSthread is an official object-oriented thread operation technology provided by Apple. It is the upper layer encapsulation of threads and tends to be the bottom layer. Simple and convenient, you can directly operate thread objects, less frequent use.

Creating a thread You can create a thread in the following three ways

  • throughinitInitialization mode
  • throughdetachNewThreadSelectorConstructor mode
  • throughperformSelector...Method creation, primarily for fetchingThe main thread, and background threads
- (void)cjl_createNSThread{NSString *threadName1 = @"NSThread1"; NSString *threadName2 = @"NSThread2"; NSString *threadName3 = @"NSThread3"; NSString *threadNameMain = @"NSThreadMain"; // Method 1: Initialization mode, NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething: object:threadName1); [thread1 start]; [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:threadName2]; // Select 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 // whether the thread isCancelled. - thread.isFinished // whether the thread is complete. - thread.isMainThread // whether the thread is the main thread - thread.threadPriority // Priority of a thread. The value ranges from 0.0 to 1.0. The default priority is 0.5Copy the code

Class methods Common class methods are the following:

  • currentThread: Gets the current thread
  • sleep...: blocking thread
  • exit: Exit thread
  • mainThread: Gets the main thread
- (void)cjl_NSThreadClassMethod{// currentThread [NSThread currentThread]; // If number=1, NSLog(@"%@", [NSThread currentThread]); [NSThread sleepForTimeInterval:2]; [NSThread sleepUntilDate:[NSDate date]]; // other [NSThread exit]; // Exit thread [NSThread isMainThread]; // Check whether the threaded thread is the active thread [NSThread isMultiThreaded]; NSThread mainThread = [NSThread mainThread]; // mainThread object NSLog(@"%@", mainThread);Copy the code

GCD

dispatch_after

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

dispatch_once

- (void)cjl_testOnce{/* dispatch_once Ensures that the code in the block is executed only once during App running. Application scenarios: Singleton, method-swizzling */ 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 executes it repeatedly until all processing has finished. It is used to calculate the size of each control in advance after pulling network data to prevent calculation during drawing. Improved form sliding fluency - Add to serial queue - Sequential execution - Add to primary queue - deadlock - Add to concurrent queue - Out-of-order execution - Add to global queue - out-of-order execution */ dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL); NSLog (@ "before dispatch_apply"); /** param1: indicates the number of repetitions param2: indicates the queue to be appended param3: */ 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: scheduling group task execution group, can listening task group, and set up the wait time application scenarios: */ dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); Dispatch_group_async (group, queue, ^{NSLog(@" request done "); }); Dispatch_group_async (group, queue, ^{NSLog(@" request two done "); }); Dispatch_group_notify (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 occur in pairs. */ 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 done "); dispatch_group_leave(group); }); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request two done "); dispatch_group_leave(group); }); Dispatch_group_notify (dispatch_get_main_queue(), ^{NSLog(@" refresh interface "); }); }Copy the code
  • Add a timeout to method twodispatch_group_wait
- (void)cjl_testGroup3{* long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout) group: Timeout: Timeout to wait (how long to wait) - DISPATCH_TIME_NOW means that the dispatch group is not waiting for completion - DISPATCH_TIME_FOREVER blocks the current dispatch group until completion: */ dispatch_group_t group = dispatch_group_create(); */ 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 done "); dispatch_group_leave(group); }); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request two done "); 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 task on time "); }else{NSLog(@" timeout "); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@" refresh interface "); }); }Copy the code

dispatch_barrier_sync & dispatch_barrier_async

Fence functions are used in two main scenarios: serial queue and concurrent queue

- (void)cjl_testBarrier{/* dispatch_barrier_sync & dispatch_barrier_async Application scenario: After the tasks added to the queue, such as synchronization lock, are completed, the tasks added to the queue behind the fence are added to the queue. - dispatch_barrier_async: dispatch_barrier_sync: dispatch_barrier_sync: - dispatch_barrier_async can control the order in which tasks are executed in the queue. - Dispatch_barrier_sync not only blocks the queue, Also blocks thread execution (use as little as possible) */ [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(@" delay task for 2s 1 - %@", [NSThread currentThread]); }); NSLog(@" first end - %@", [NSThread currentThread]); // The barrier function groups 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(@" delay task for 2s 2 - %@", [NSThread currentThread]); }); NSLog(@" end second - %@", [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(@" delay task for 2s 1 - %@", [NSThread currentThread]); }); NSLog(@" first end - %@", [NSThread currentThread]); // Because the tasks in the concurrent queue are executed 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(@" delay task for 2s 2 - %@", [NSThread currentThread]); }); NSLog(@" end second - %@", [NSThread currentThread]); }Copy the code

dispatch_semaphore_t

Semaphores are primarily used as synchronization locks to control the maximum number of concurrent GCDS

- (void)cjl_testSemaphore{/* Application scenarios: dispatch_semaphore_create() : dispatch_semaphore_wait() : dispatch_semaphore_wait() : Wait for the semaphore, the semaphore decreases by 1. If the semaphore is < 0, the current thread is blocked based on the incoming wait time. If the wait is permanent, the next action will wait for signal. - dispatch_semaphore_signal() : the semaphore is released and the semaphore is incremented by 1. */ 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]); }); } // dispatch_semaphore_t 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 timers that do not rely on RunLoop and are more accurate than NSTimer

- (void)cjl_testSource{/* dispatch_source GCDTimer typically uses NSTimer in iOS development to handle timing logic, but NSTimer relies on Runloop, which can run in different modes. If NSTimer is added in one mode, the timer hangs up while Runloop is running in another mode; If Runloop is blocked, NSTimer firing is delayed until the next Runloop cycle. The dispatch_source is a basic data type that can be used to listen for some low-level system events. Timer event Source, used to generate periodic notifications or callbacks. -signal Dispatch Source: Listen for Signal event Source, which can be used when UNIX signals occur. -Mach port Dispatch Source -Mach port Dispatch Source -Mach port Dispatch Source: -custom Dispatch Source: -dispatch_source_create: -dispatch_source_create: -dispatch_source_set_event_handler: Sets the data source callback. -dispatch_source_merge_data: -dispatch_source_get_data: obtain the event source data. -dispatch_resume: continue. -dispatch_suspend: suspend. -dispatch_cancle: cancel. Dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //2. Create timer dispatch_source_t timer = dispatch_source_create(dispatch_source_timer, 0, 0, queue); Dispatch_source_set_timer (timer, DISPATCH_TIME_NOW, 2.0*NSEC_PER_SEC, 0.1*NSEC_PER_SEC); Dispatch_source_set_event_handler (timer, ^{NSLog(@"GCDTimer")); }); //5. By default, dispatch_resume(timer) needs to be activated. }Copy the code

NSOperation

NSOperation is a higher level of encapsulation based on GCD, and NSOperation needs to work with NSOperationQueue to implement multi-threading.

NSOperatino implements multithreading as follows:

  • 1.Create a task: Encapsulates the operations to be performed in an NSOperation object.
  • 2,Create a queue: Creates NSOperationQueue.
  • 3,Adds the task to the queue: Adds the NSOperation object to the NSOperationQueue.
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation::) object:@"CJL"]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:op]; } - (void)handleInvocation:(id)operation{ NSLog(@"%@ - %@", operation, [NSThread currentThread]); }Copy the code

Note that NSOperation is an abstract class, and you need to use its subclasses in three ways:

  • 1. Use subclassesNSInvocationOperation
- (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, the tasks in the block are executed on the main thread, while the tasks added with 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 NSOperationTo encapsulate tasks by implementing internal corresponding methods.
/ / * * * * * * * * * custom inherited from NSOperation subclasses of * * * * * * * * * @ interface CJLOperation: NSOperation @end @implementation CJLOperation - (void)main{ for (int i = 0; i < 3; I++) {NSLog(@" subclass of NSOperation: %@",[NSThread currentThread]); }} @end //********* use ********* - (void)cjl_testCJLOperation{// Use a subclass derived from NSOperation, Then override its main method. CJLOperation *operation = [[CJLOperation alloc] init]; [operation start]; }Copy the code

NSOperationQueue

NSOperationQueue adds the transaction

NSOperationQueue has two types of queues: primary queue and other queues. Other queues contain both serial and concurrent queues.

  • The home side columns:The home side columnThe mission on theThe main threadTo perform.
  • Other queue (non-primary queue) : Tasks added to the ‘non-queue’The default is concurrency, enable multi-threading.
- (void)cjl_testNSOperationQueue{/* NSInvocationOperation and NSBlockOperation - the former is similar to target - the latter is similar to block - functional programming, business logic code is more readable NSOperationQueue is executed asynchronously, NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{ NSLog (@ "task 1 -- - % @", [NSThread currentThread]);}]; // Add transaction [bo addExecutionBlock:^{NSLog(@" task 2————%@",[NSThread currentThread]);}]; Bo.pletionblock = ^{NSLog(@" done!!") ); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo]; NSLog(@" transaction added to NSOperationQueue"); }Copy the code

Setting the 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 the Priority

- (void)cjl_testOperationQuality{/* NSOperation setting the priority will only make the CPU more likely to call, It is not necessary to set high to complete all tasks first - do not use sleep - high priority task 1 precedes low priority task 2 - use sleep for delay - 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 (@ "the 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 (@ "the 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 the number of concurrent requests

// Set concurrency - (void)cjl_testOperationMaxCount{/* In GCD only semaphore can set concurrency and 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:^{NSThread sleepForTimeInterval:2];  NSLog(@"%d-%@",i,[NSThread currentThread]); }]; }}Copy the code

Add the dependent

// Add 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 dat1, request dat2 ");}]; [bo2 addDependency:bo1]; [bo3 addDependency:bo2]; [queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:YES]; NSLog at sign "done? I'll do something else "); }Copy the code

Interthread communication

// 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