IOS thread alive

Time:2022-6-12

During development, time-consuming operations are often put into sub threads for execution to improve application performance. When the task in the child thread is completed, the thread is destroyed immediately.

If we need to frequently execute tasks in sub threads during development, frequent creation and destruction of threads will cause a waste of resources, which is not in line with our original intention. At this point, we need to keep the thread alive to ensure that the thread wakes up when it should handle things and sleeps when it is idle.

We know that runloop can wake up to execute tasks when transactions need to be processed, and sleep when idle to save resources. This feature can be used to handle thread liveness and control thread life cycle.

explore

- (void)viewDidLoad {
    [super viewDidLoad];
    
    LSThread *thread = [[LSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [thread start];
}

- (void)run {
    NSLog(@"func -- %s   thread -- %@", __func__, [NSThread currentThread]);
    [[NSRunLoop currentRunLoop] run];
    Nslog (@ ---- end -- ");
}

LSThreadInherited fromNSThread, rewrittendeallocmethod

- (void)dealloc {
    NSLog(@"%s", __func__);
}

Results after execution:

IOS thread alive

Thread alive failed

You can see that the thread is not alive:

  • Although runloop is started, the following end log is still executed
  • The thread was destroyed after execution

To ensure that the thread will not be destroyed after execution, the thread can be strongly referenced

self.thread = [[LSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[self.thread start];

But this does not solve the runloop problem. Now that runloop has been started, why not keep it running continuously?

Let’s take a lookrunDefinition of method

If no input sources or timers are attached to the run loop, this method exits immediately.

This means that if no sources or timers are attached to runloop, this method will exit immediately.

Then we can add a source or timer to runloop to solve this problem

[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];

Running again does not end the log, and the thread is kept alive successfully.

IOS thread alive

Thread alive successfully

Let’s continue to improve our requirements. When the controller holding the thread is destroyed, the new child thread should also be destroyed and added to the controllerdealloc

- (void)dealloc {
    Nslog (@ - -- destroy controller ---%s ", \u func\u);
}

When the controller appears, a child thread is created. When the controller disappears, the console outputs the following

IOS thread alive

Circular reference occurs

Neither the controller nor the child thread was destroyed. view code

self.thread = [[LSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

Here, the controller strongly references the thread, and the thread holds the controller self internally, resulting in a reference loop.

To break this reference loop, you can use block

self.thread = [[LSThread alloc] initWithBlock:^{
        NSLog(@"func -- %s   thread -- %@", __func__, [NSThread currentThread]);
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
        Nslog (@ ---- end -- ");
    }];

results of enforcement

IOS thread alive

Resolve circular references

If the controller is released successfully, but the child thread is not destroyed, the child thread becomes a global thread. That’s the start of runloop.

There are three ways to start runloop

- (void)run;

- (void)runUntilDate:(NSDate *)limitDate;

- (void)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;

runMethod internalNSDefaultRunLoopModeRepeated calls in moderunMode:beforeDate:method.

runUntilDate:Method can set the timeout time. Before the timeout time expires, runloop will run continuously. During this period, runloop will process data from sources, andrunThe same will happen inNSDefaultRunLoopModeRepeated calls in moderunMode:beforeDate:method.

runMode:beforeDate:The method runloop will run once. When the timeout expires or the first source is processed, the runloop will exit.

aboutrunMethod the apple documentation states that this method should not be used if you want to exit runloop.

If runloop does not have input sources or additional timers, runloop will exit. Although this can exit the runloop, apple does not recommend it. The system may add some input sources to the runloop of the current thread. Therefore, manually removing the input source or timer does not guarantee that the runloop will exit.

Then the problem is clear. We shouldn’t userunMethod to start runloop, because it creates a loop that will not exit. The child threads using this method cannot be destroyed naturally. We can likerunSame userunMode:beforeDate:Method to create a child thread that meets our criteria:

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

Put it in awhileIn the loop, a global flag of whether to stop runloop is used to assist in handling the life cycle of threads

__weak typeof(self) weakSelf = self;
    self.thread = [[LSThread alloc] initWithBlock:^{
        NSLog(@"func -- %s   thread -- %@", __func__, [NSThread currentThread]);
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//        [[NSRunLoop currentRunLoop] run];
        while (!weakSelf.isStopedThread) {
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        Nslog (@ ---- end -- ");
    }];

Method to stop runloop

- (void)stop {
    self.isStopedThread = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
}

One thing to note here is that the stop operation must be executed in our target thread. For example, we directly callstopThe method can not achieve the desired effect becausestopBy default, it is executed in the main thread. If the target thread is not obtained, the stop is invalid.

- (void)stopAction {
    [self performSelector:@selector(stop) onThread:self.thread withObject:nil waitUntilDone:YES];
    self.thread = nil;
}

Called on the current threadstop, our goal is achieved. The runloop is successfully ended and the thread is destroyed.

IOS thread alive

Thread preservation completed

Recommended Today

A front-end developer's Vim is the same as an IDE

Here is my new configurationjaywcjlove/vim-webI've been grinding it, and it's basically ready to use. Take it out and cheat the star Install The latest version of Vim 7.4+ uses (brew install macvim) installation, vim version updatebrew install macvim –override-system-vim View configuration locations # Enter vim and enter the following characters :echo $MYVIMRC download vim-web Download […]