This article was first published on my personal blog: blog.shenyuanluo.com. Welcome to subscribe if you like.

Consider the following code. What is the final output?

  1. Example 1:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        NSLog(@"1 - % @", [NSThread currentThread]);
    
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                       withObject:nil];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:One, two, three, four
    • The reason:becauseperformSelector:withObject:The specified selector is executed immediately on the current thread.
  2. Example (2) :
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1 - % @", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                       withObject:nil
                       afterDelay:0];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:One, two, four
    • The reason:becauseperformSelector:withObject:afterDelay:It registers a timer in the RunLoop, and in the child thread, the RunLoop is not enabled (by default), so it does not output3. The official website API is explained as follows:
  3. Example 3:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1 - % @", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                       withObject:nil
                       afterDelay:0];
            [[NSRunLoop currentRunLoop] run];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    	
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:One, two, three, four
    • The reason:Due to the[[NSRunLoop currentRunLoop] run];The RunLoop object corresponding to the current child thread is created and started, so it can be executedtestMethods; andtestAfter execution, the timer registered in the RunLoop is invalid, so you can still output4⑥ Examples
  4. Example 4:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1 - % @", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                         onThread:[NSThread currentThread]
                       withObject:nil
                    waitUntilDone:YES];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    	
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:One, two, three, four
    • The reason:becauseperformSelector:onThread:withObject:waitUntilDone:Executes on the specified thread, and executes according to the parameterswaitDeal with it. Pass it hereYESIndicates that it will be blocked immediatelySpecified threadAnd executes the specifiedselector. The official API is explained as follows:
  5. Example 5:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1 - % @", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                         onThread:[NSThread currentThread]
                       withObject:nil
                    waitUntilDone:NO];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    	
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:One, two, four
    • The reason:becauseperformSelector:onThread:withObject:waitUntilDone:Executes on the specified thread, and executes according to the parameterswaitDeal with it. Pass it hereNOIndicates that the block is not immediately blockedSpecified threadIt will beselectorAdded to the specified threadRunLoopWaiting for an opportunity to execute. (In this example, the child thread RunLoop is not started, so there is no output3The official website API is explained as follows:
  6. Example 6:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1 - % @", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                         onThread:[NSThread currentThread]
                       withObject:nil
                    waitUntilDone:NO];
            [[NSRunLoop currentRunLoop] run];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    	
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:1,2,3
    • The reason:Due to the[[NSRunLoop currentRunLoop] run];The RunLoop object corresponding to the current child thread has been created and started, so it is ready to executetestMethods; buttestThe RunLoop does not end after the method is executed. (Using this startup method, the RunLoop will continue running, processing data from the input source during this time, and will run in theNSDefaultRunLoopModeRepeat the call in moderunMode:beforeDate:Method) so cannot continue output4.
  7. Example 7:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1 - % @", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                         onThread:[NSThread currentThread]
                       withObject:nil
                    waitUntilDone:NO];
            [[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    	
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:1,2,3
    • The reason:Due to the[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];The RunLoop object corresponding to the current child thread has been created and started, so it is ready to executetestMethods; buttestThe RunLoop does not end after the method is executed. (With this startup method, you can set a timeout, and the RunLoop will run until the timeout expires, during which time the RunLoop will process data from the input source, and will run in theNSDefaultRunLoopModeRepeat the call in moderunMode:beforeDate:Method) so cannot continue output4.
  8. Examples today:
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1 - % @", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"2 - % @", [NSThread currentThread]);
            [self performSelector:@selector(test)
                         onThread:[NSThread currentThread]
                       withObject:nil
                    waitUntilDone:NO];
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                     beforeDate:[NSDate distantFuture]];
            NSLog(@4 - % @ "", [NSThread currentThread]);
        });
    }
    	
    - (void)test
    {
        NSLog(@"3 - % @", [NSThread currentThread]);
    }
    Copy the code
    • Output result:One, two, three, four
    • The reason:Due to the[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];The RunLoop object corresponding to the current child thread has been created and started, so it is ready to executetestMethods; andtestThe RunLoop terminates immediately after the method is executed. (With this startup, the RunLoop runs once until the timeout expires or the firstinput sourceIs processed, RunLoop exits) so you can continue output4.

Summary:

  1. So we’re going to use performSelector
    • The common method to perform is the nsobject. h header:
    - (id)performSelector:(SEL)aSelector;
    - (id)performSelector:(SEL)aSelector withObject:(id)object;
    - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
    Copy the code
    • Perform, which is the method under the nsrunloop. h header:
    - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
    - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
    Copy the code
    • We can specify threads’ perform, which is the method under the NSThread header:
    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
    - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg;
    Copy the code
  2. RunLoop Exit:
    • use- (void)run;When started, the RunLoop runs forever, processing data from the input source, and running in theNSDefaultRunLoopModeRepeat the call in moderunMode:beforeDate:Methods;
    • use- (void) runUntilDate (limitDate NSDate *);Start, you can set a timeout period, and the RunLoop will run until the timeout period is reached, during which time the RunLoop will process data from the input source, and will also run inNSDefaultRunLoopModeRepeat the call in moderunMode:beforeDate:Methods;
    • use- (void)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;On startup, the RunLoop will run once, and either the timeout expires or the first oneinput sourceIs processed, the RunLoop exits.
  3. See this blog post for more information on how NSRunLoop exits

reference

  1. Exit method of NSRunLoop