Alec
Alec

Reputation: 483

Why isn't this NSArray deallocated under ARC?

I load a static property list from my main bundle. Simple enough. However the property list is not deallocated after I set it to nil. Why is this? The allocated memory is easy to monitor, as the property list is huge.

NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0; 
NSError *error = nil; 
NSArray *myArray = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfFile:bundleFilePath] 
                                                                          options:NSPropertyListImmutable
                                                                           format:&format
                                                                            error:&error];

myArray = nil; 
// Still takes up allocated space (as seen in memory monitor via instruments).

With autorelease pool:

- (void)someMethodCalledOnTheMainThread
{ 
    @autoreleasePool {
        NSString *bundleFilePath = @"..."; 
        NSPropertyListFormat format = NSPropertyListBinaryFormat_v1_0; 
        NSError *error = nil; 
        NSArray *myArray = [NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfFile:bundleFilePath] 
                                                                          options:NSPropertyListImmutable
                                                                           format:&format
                                                                            error:&error];
    }

}

Upvotes: 0

Views: 771

Answers (1)

ughoavgfhw
ughoavgfhw

Reputation: 39925

The array has been autoreleased. According to the memory management guidelines, the propertyListWith... method will return an autoreleased object, which means there is a reference to the object which will be released at some point in the future. Your code is basically equivalent to this (without ARC):

NSArray *myArray = [[NSPropertyListSerialization propertyListWith/*stuff*/] retain];
// 1 reference + 1 autoreleased reference
[myArray release]; myArray = nil;
// 1 autoreleased reference

Since you didn't drain the autorelease pool containing the array, there is still a reference to it. It will be deallocated at some point in the future, most likely when the current run loop ends or thread exits. If you want to force the array to be released sooner, you can create your own autorelease pool.

@autoreleasepool {
    NSArray *myArray = [[NSPropertyListSerialization propertyListWith/*stuff*/] retain];
    // 1 reference + 1 autoreleased reference
    [myArray release]; myArray = nil;
    // 1 autoreleased reference
} // no references. The array is deallocated

Now, the array is released at a known time. Note that any other autoreleased objects created within the pool's scope will also be released, which could cause problems if you don't plan for it. This gives you more explicit control over when the array is deallocated, but most likely will not create a noticeable difference unless you are creating this property list within a loop, since the run loop would soon drain its autorelease pool after you return.

Upvotes: 2

Related Questions