Why does the position of @autoreleasepool matter?

2019-08-03 18:05发布

问题:

I'm having trouble understanding how @autoreleasepool work. Consider the following example in which I am creating an AVAssetReader for an audiofile. To make the memory impact matter, I repeated this step 1000 times.

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

void memoryTest() {
    NSURL *url = [[NSURL alloc] initWithString:@"path-to-mp3-file"];
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
    AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:NULL];
}

int main(int argc, const char * argv[]) {

    // Breakpoint here (A)

    @autoreleasepool {
        for(int i = 0; i < 1000; i++) {
            memoryTest();
        }
    }

    // Breakpoint here (B)

    return 0;
}

Before and after the loop at breakpoint A and breakpoint B I took a look at the memory usage of my application in the Xcode Debug Navigation. At point A my App consumes about 1.5MB of memory. At point B it is about 80MB. Interestingly the memory usage at B drops to about 4MB when I put the autoreleasepool inside the loop like so:

for(int i = 0; i < 1000; i++) {
    @autoreleasepool { memoryTest(); }
}

Why does the position matter? (The breakpoint - in both cases - is outside of the autoreleasepool!) In either case the consumed memory at point B is proportional to the number of loops. Am I missing something else here to free up its memory?

I thought of the memory chart in the navigator to be delayed, but adding usleep just before the breakpoint does not change anything. If I change memoryTest() to

void memoryTest() {
    NSURL *url = [NSURL URLWithString:@"path-to-mp3-file"];
}

the position of @autoreleasepool does not matter. Isn't that weird?

I am using Xcode 6, OS X SDK 10.10 with ARC enabled.

回答1:

Temporary memory is being created and autorelease'ed by the code inside the loop by your code and the API calls it makes. This memory is not released until the pool is drained. The usual point it is is drained is in the run loop. But your loop does not allow the run loop to execute so by placing the @autoreleasepool inside the loop the pool can drain as your code runs.