I use performSelector API very little in my development, but when I look at some third parties, I find that third-party authors use performSelector API a lot. It is my understanding that you can decouple to a certain extent without introducing related classes. But I’ve had some problems with it recently. As a result, I checked some blogs and did my own verification. I will record it here.

Let’s start with some code:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1");
        [self performSelector:@selector(testPerform) withObject:nil afterDelay:0]; // NSLog(@"3");
    });
}
- (void)testPerform{
    NSLog(@"2");
}
Copy the code

The result is as follows: no 2 is printed, only 1 and 3 are printed.

testPerform

Official notes:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1");
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        [self performSelector:@selector(testPerform) withObject:nil afterDelay:0]; // NSLog(@"3");
    });
}
Copy the code

By fetching the current runloop, the system returns the current runloop, or if it does not have one, it will return after creation. However, when a runloop is added, only 1 and 3 will be printed, and no 2 will be printed. Before and after the [self performSelector:@selector(testPerform) withObject:nil afterDelay:0] method call, print the runloop object through the console, and actually see the method call, A timer source is added to the runloop.

Runloop comparison before and after:

testPerform
[runloop run];
testPerform

But, the question is again, if the runloop is added, and it runs, why will the 3 print? Why does the 3 out of the loop print out? This problem can be seen by the observer of the added runloop, because the runloop exited after performing testPerform. So the next three pages are printed.

Observer print:

testPerform
[self performSelector:@selector(testPerform) withObject:nil afterDelay:0]

How do I keep runloop from exiting? Add event sources or timer temers to the current runloop, and the current runloop will not exit, but will sleep when no work is needed.

repeats
[runloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];

– (void)performSelector: SEL aSelector withObject: nullable ID anArgument afterDelay: NSTimeInterval delay inModes:(NSArray

*)modes; This method has a parameter to set mode, which can be used to set the mode in which the timer is executed, readers can check themselves.

Add runloop observer code:

- (void)addObserver
{
    /*
     kCFRunLoopEntry = (1UL << 0),1
     kCFRunLoopBeforeTimers = (1UL << 1),2
     kCFRunLoopBeforeSources = (1UL << 2), 4
     kCFRunLoopBeforeWaiting = (1UL << 5), 32
     kCFRunLoopAfterWaiting = (1UL << 6), 64
     kCFRunLoopExit = (1UL << 7),128
     kCFRunLoopAllActivities = 0x0FFFFFFFU
     */
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case 1:
            {
                NSLog(@"Enter the runloop");
            }
                break;
            case 2:
            {
                NSLog(@"timers");
            }
                break;
            case 4:
            {
                NSLog(@"sources");
            }
                break;
            case 32:
            {
                NSLog(@"About to go to sleep.");
            }
                break;
            case 64:
            {
                NSLog(@"Wake up");
            }
                break;
            case 128:
            {
                NSLog(@"Quit");
            }
                break;
            default:
                break; }}); CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes); // Add the observer to common mode so that there are callbacks in both default mode and UITrackingRunLoopMode. self.obsever = observer; CFRelease(observer); }Copy the code

This record is my own understanding, the level is limited, if there is any mistake, please criticize and correct, we will revise as soon as possible.


Reference acknowledgments:

Summary of iOS basic principles – RunLoop

A little talk about performSelector

Exit method of NSRunLoop