The introduction

Recently, a bigshot asked me what I knew about Autoreleasepool. I thought I knew, but when I asked a little deeper, I was speechless. After careful consideration, I decided to reorganize this problem based on previous knowledge. Of course, practice is essential.

  • What does autoreleasepool do in the main function?
  • Autoreleasepool – Autoreleasepool
  • When is autoreleasepool needed in an ARC environment? When is the autoreleasepool variable released when it is not used?

With these three questions in mind, let’s think about the following.

The body of the

Autoreleasepool release time:

It can be divided into two cases: manual intervention of release timing and automatic release by the system.

Manual intervention with release timing – Specifying that autoReleasepool is released when the current scope braces end.

The system automatically de-releases — autoreleasepool is not specified manually

Regardless of whether the above is true or not, it occurred to me that autoreleasepool in the main function could be used in one of two ways:

1. Default AutoReleasepool in the main thread.

2. An Autoreleasepool for the entire App relative to iOS.

Other explanations can be found on the Internet, so we can do a little experiment here.

The first point is easy to verify. Comment out autoreleasepool in the main function and run it

for (int i = 0; i < 10e5 * 2; i++) {
    NSString *str = [NSString stringWithFormat:@"hi + %d", i];
}
NSLog(@"finished!");Copy the code

The actual results show that memory fluctuations make no difference:

  • Uncommented Autoreleasepool in the Main function

  • Comment autoreleasepool in the Main function

So we can think the second one is right, but later we thought it is not right. How can the code related to system memory management be in the program? It is not in line with Apple’s style. It turns out that my own assumptions were obviously wrong, so what does it do? More on that later, but let’s verify the timing of the release.

Again, add autoreleasepool to the for loop:

for (int i = 0; i < 10e5 * 2; i++) {
    @autoreleasepool {
        NSString *str = [NSString stringWithFormat:@"hi + %d", i];
    }
}
NSLog(@"finished!");Copy the code

I’m sure those of you who know a little bit better already know the results:

The memory allocated for temporary variables has been released smoothly, so the conclusion is the cognition that we see at the top, right? By default, each Runloop already creates an Autoreleasepool, so we’ve added what amounts to a nested (easy to understand) autoreleasepool, without knowing when the autoreleasepool itself will be released. Here’s another quiz:

This time add a Runloop Observer to the code, obtain the status change of Runloop to confirm the release time, the code is as follows:

// Add a listener - (void)addRunLoopObserver {// 1. Create a listener CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler (kCFAllocatorDefault kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { switch (activity) {case kCFRunLoopEntry:
                NSLog(@"Enter the RunLoop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"Timer event about to be processed");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"Source event about to be processed");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"Going to sleep.");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"Awakened");
                break;
            case kCFRunLoopExit:
                NSLog(@"Quit the RunLoop");
                break;
            default:
                break; }}); // 2. Add listener CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes); }Copy the code

In addition, the above method runs twice in a row without manually adding autoReleasepool, which looks something like this:

- (void)test1 {

    NSLog(@"test1 begin!");
    for (int i = 0; i < 10e5 * 2; i++) {
        //@autoreleasepool {
            NSString *str = [NSString stringWithFormat:@"hi + %d", i];
        //}
    }
    NSLog(@"test1 finished!");
}

- (void)test2 {

    NSLog(@"test2 begin!");
    for (int i = 0; i < 10e5 * 2; i++) {
        //@autoreleasepool {
            NSString *str = [NSString stringWithFormat:@"hi + %d", i];
        //}
    }
    NSLog(@"test2 finished!");
}Copy the code

It looks like this:

It is clear that all memory is not freed until the Runloop completes a loop, even if local variables are out of scope and must wait for the Runloop to complete.

Again, add autoReleasepool manually to observe the release timing.

The results were unexpected and reasonable. Even if the Runloop does not complete the loop, memory is freed

conclusion

@autoreleasepool{}Copy the code

Is equivalent to

void *context = objc_autoreleasePoolPush(); // the code in {} is objc_autoreleasePoolPop(context);Copy the code

Objc_autoreleasePoolPop () is called every time {} is emitted, so it is released directly. Of course, the system automatically creates autoreleasepool in the same way, but at different times: The thread corresponds to the Runloop one by one, and the Runloop corresponds to the autoreleasepool created by the system one by one, so the AutoReleasepool is released either when the Runloop completes a loop or when the thread is closed. Manually added autoreleasepool is also managed. Here’s a personal summary of the nature of @Autoreleasepool {} :

It’s basically just one class :AutoreleasePoolPage

Two functions: objc_autoreleasePoolPush() and objc_autoreleasePoolPop()

Operation mode: Autoreleasepool is a bidirectional list of autoreleasePoolPage classes. When @Autoreleasepool {is run, objc_autoreleasePoolPush() will be called. Runtime adds a nil object to the current AutoreleasePoolPage as a sentinel. Objects created in {} are recorded to the top pointer of the stack. When @Autoreleasepool {} is run, Objc_autoreleasePoolPop (sentry) will be called, and runtime will send a release message to the object recorded in AutoreleasePoolPage until the sentry position completes a complete operation.

In addition, according to official documents:

Threads

If you are making Cocoa calls outside of the Application Kit’s main thread — for example If you create a foundation-only Application or if you detach a thread — you need to create your own autorelease pool……

The auto-release pool in the main thread is created automatically, and the auto-release pool in the child thread needs to be created manually, but in fact, the common multi-thread management methods (GCD, NSOprationQueue, NSThread) have been handled for us. The NSThread after iOS7 AutoreleasePool automatically create threads, this can’t find the records in official document, reference StackOverflow: stackoverflow.com/questions/2…

AutoreleasePool does not affect the performance of the AutoreleasePool function. It even runs 2 seconds faster than AutoreleasePool.

Back to the original question about the role of autoreleasepool in the main function, I looked through a lot of information and the most popular answer on StackOverflow is that it doesn’t work… Take it that way for now.. Hope to have an understanding of the students can explain ~

In practice, the scenario is clear. It is best to create temporary variables manually when there are a large number of temporary variables in the program.

The most common time to have a large number of variables is obviously looping/traversing. The for loop we use, and enumerate, is related to autoreleasepool. Autoreleasepool is not automatically created for the for loop. Autoreleasepool has already been created for Enumerate. It should be noted that high concurrency enumerate often causes unexpected problems, such as objects being released prematurely. Therefore, it is recommended to use a for loop for high concurrency situations (with higher performance than Enumerate). Add autoReleasepool manually.

An App mentioned in my previous articles: Live Streaming partner is the mobile terminal for bullet screen to carry out high concurrent calculation, word segmentation, comparison.. The use of Autoreleasepool is obviously improved when using douyu barrage server “Fried fish”. Welcome to Star: github.com/syik/Bullet…