//toBeReclaimed = nil;


#1

[This question regards memory stuff so might be more suitable for the Chapter 20 thread - however, I’m referencing material from Chapter 21. Sorry if this is in the wrong place]

At the end of the Filtering section of NSArray/NSMutableArray, I tried commenting out toBeReclaimed = nil. I think I’m misunderstanding something and I’d love it if someone could explain this to me. I assumed that because toBeReclaimed wasn’t now being set to nil, the elements of the array would still have an owner (and a retain count). Therefore, I assumed that I wouldn’t see the deallocation message for these objects when allAssets was set to nil, because they’d still have an owner - but they all get deallocated as normal. Can someone explain this to me? (I realise I’m probably thinking way too much about this but it will play on my mind if I don’t understand it). Many thanks


#2

I know this was asked a while ago but it had me intrigued enough to look into it.

From what I can see, the array toBeReclaimed, was never created using alloc so is auto released. The array is created as the result of the filteredArrayUsingPredicate: method which would have sent the autorelease message before it was returned and assigned to toBeReclaimed.

So it really doesn’t matter if you set the array to nil or not. Once allAssets is set to nil there is no longer an owner of the assets due to the auto release.

Perhaps someone can verify this, but that’s what I believe is happening.


#3

I just wanted to say I had this question too. So it wouldn’t hurt to clarify it :slight_smile:

I did a little experiment and tried using the following code at the end of the program

[code] NSArray *toBeReclaimed = [[NSArray alloc] initWithArray:[allAssets filteredArrayUsingPredicate:predicate]];
NSLog(@“tobeReclaimed: %@”, toBeReclaimed);

    NSLog(@"Giving up ownership of array");
    
    executives = nil;
    allAssets = nil;
    employees = nil;

    NSLog(@"Giving up ownership of toBeReclaimed");
    toBeReclaimed = nil;

[/code]

and the deallocation output ended up being

2012-01-07 09:55:39.987 BMITime[796:707] Giving up ownership of array 2012-01-07 09:55:39.988 BMITime[796:707] deallocating <Laptop 7: $119, unassigned > 2012-01-07 09:55:39.989 BMITime[796:707] deallocating <Employee 0: $0 in assets> 2012-01-07 09:55:39.989 BMITime[796:707] deallocating <Employee 4: $0 in assets> 2012-01-07 09:55:39.990 BMITime[796:707] deallocating <Employee 8: $0 in assets> 2012-01-07 09:55:39.990 BMITime[796:707] Giving up ownership of toBeReclaimed 2012-01-07 09:55:39.992 BMITime[796:707] deallocating <Employee 1: $153 in assets> 2012-01-07 09:55:39.993 BMITime[796:707] deallocating <Laptop 9: $153, unassigned > 2012-01-07 09:55:39.994 BMITime[796:707] deallocating <Employee 9: $136 in assets> 2012-01-07 09:55:39.994 BMITime[796:707] deallocating <Laptop 8: $136, unassigned > 2012-01-07 09:55:39.995 BMITime[796:707] deallocating <Employee 5: $136 in assets> 2012-01-07 09:55:39.996 BMITime[796:707] deallocating <Laptop 3: $51, unassigned > 2012-01-07 09:55:39.996 BMITime[796:707] deallocating <Laptop 5: $85, unassigned > 2012-01-07 09:55:39.997 BMITime[796:707] deallocating <Employee 7: $34 in assets> 2012-01-07 09:55:39.998 BMITime[796:707] deallocating <Laptop 2: $34, unassigned > 2012-01-07 09:55:39.998 BMITime[796:707] deallocating <Employee 6: $119 in assets> 2012-01-07 09:55:39.999 BMITime[796:707] deallocating <Laptop 6: $102, unassigned > 2012-01-07 09:55:40.000 BMITime[796:707] deallocating <Laptop 1: $17, unassigned > 2012-01-07 09:55:40.000 BMITime[796:707] deallocating <Employee 3: $68 in assets> 2012-01-07 09:55:40.001 BMITime[796:707] deallocating <Laptop 4: $68, unassigned > 2012-01-07 09:55:40.002 BMITime[796:707] deallocating <Laptop 0: $0, unassigned >

Which is what we were both expecting to see so I believe MarkReid’s explanation is correct.


#4

Mhm, I am not sure that Marks explanation is correct - it sounds plausible for me too, but how about this? In this case we explicitly use alloc/init and the result is the same. May be it has something to do with they are both global variables and not ivars.

cu
Vertex


#5

This will start to make more sense once you move away from sample apps where you edit the main() function and into the land of Cocoa and Cocoa Touch development.

In these examples, there is a scoping @autoreleasepool {} that wraps all of the code. These local vars (not global or ivars - they’re local to the main() function), go out of scope at the end of the autoreleasepool chunk (they would anyway when the method in which they’re declared returns, but main() is something of a special case).

A local variable falling out of scope is tantamount to setting it to nil as far as memory management is concerned. While autorelease pools do indeed have an impact on the timing of object deallocation, it’s not the autorelease pool itself that’s causing the deallocation here, but merely a local pointer being destroyed as it leaves the scope in which it was declared.

Whether an object is created using +alloc explicitly (as in [[NSDate alloc] init]) or implicitly by using a convenience constructor (such as [NSDate date]) no longer has an effect on memory management under ARC. Before ARC, there were important memory-related implications of using one vs. the other.

I’ve put together a small example to illustrate the effect of a local variable falling out of the scope in which it is declared.

BNRObject.h:

@interface BNRObject : NSObject @end

BNRObject.m:

[code]#import “BNRObject.h”

@implementation BNRObject

  • (id)init {
    self = [super init];
    if (self) {
    NSLog(@"%@ initializing.", self);
    }
    return self;
    }

  • (void)dealloc {
    NSLog(@"%@ deallocating.", self);
    }

@end[/code]

main.m:

[code]#import “BNRObject.h”
#import <Foundation/Foundation.h>

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

@autoreleasepool {
    
    BNRObject *outerObject; // useless object for demonstration        
    if (YES) { // this conditional is guaranteed
        BNRObject *innerObject; // another one
        
        NSLog(@"About to initialize outerObject.");
        outerObject = [BNRObject new]; // the same as alloc]init]
        NSLog(@"About to initialize innerObject.");
        innerObject = [BNRObject new];
        
        NSLog(@"About to leave conditional scope.");
    }
    
    NSLog(@"About to leave @autoreleasepool scope.");
}

NSLog(@"About to return from main().");
return 0;

}
[/code]

(you can also download the project here: http://ln.mw.am/DM5z)

Here is the output:
2012-01-15 14:16:37.847 Test[27824:707] About to initialize outerObject. 2012-01-15 14:16:37.848 Test[27824:707] <BNRObject: 0x100400250> initializing. 2012-01-15 14:16:37.850 Test[27824:707] About to initialize innerObject. 2012-01-15 14:16:37.851 Test[27824:707] <BNRObject: 0x100400480> initializing. 2012-01-15 14:16:37.851 Test[27824:707] About to leave conditional scope. 2012-01-15 14:16:37.852 Test[27824:707] <BNRObject: 0x100400480> deallocating. 2012-01-15 14:16:37.852 Test[27824:707] About to leave @autoreleasepool scope. 2012-01-15 14:16:37.853 Test[27824:707] <BNRObject: 0x100400250> deallocating. 2012-01-15 14:16:37.854 Test[27824:707] About to return from main().

The salient bit here is that the only strong reference to innerObject falls out of scope at the end of the if() statement, which is equivalent to setting the pointer to nil, and so innerObject immediately deallocates.

I hope that helps.