objective C memory management issue

I was just going over a piece of code the other day and then ended up compiling and running it. The result outputted on the console surprised me a bit. Here is the code-


#import 


@interface TestObject: NSObject
{
  int x;
}
@property TestObject *testObj;
-(void)run;
@end


@implementation TestObject
@synthesize testObj;
-(void)run{
    NSLog(@"running");
    TestObject *obj= [[TestObject alloc] init];
    testObj= obj;
   
    obj= nil;
    NSLog(@"testObj- %@", testObj);
    [testObj testLog];
    NSLog(@"testObj now- %@", testObj);
}
-(void)testLog{
    NSLog(@"test successful with %@", testObj);
}
-(void)dealloc{
    NSLog(@"dealloc");
}
@end
int main(int argc, const char * argv[]) {
    TestObject *test= [[TestObject alloc] init];
    [test run];
    NSLog(@"testObj in the end- %@", test.testObj);
    [test.testObj testLog];
}


And here is the output-

2019-06-02 23:49:18.224 objcmemmanage[8869:288642] running

2019-06-02 23:49:18.224 objcmemmanage[8869:288642] testObj- <TestObject: 0x7f913e501100>

2019-06-02 23:49:18.224 objcmemmanage[8869:288642] test successful with (null)

2019-06-02 23:49:18.224 objcmemmanage[8869:288642] testObj now- <TestObject: 0x7f913e501100>

2019-06-02 23:49:18.224 objcmemmanage[8869:288642] testObj in the end- <TestObject: 0x7f913e501100>

2019-06-02 23:49:18.224 objcmemmanage[8869:288642] test successful with (null)


The output, in the 3rd line, says that the testObj has been deallocated. How come it is deallocated when the testObj receives the testLog message?? The local object pointer- "obj" is assigned to testObj and then is nil-ed in the next line. But, since we are using ARC, shouldn't testObj, which happens to also be an ivar, continue owning the TestObject from that point on? My understanding has been that ARC, during compilation, places in the necessary memory management code with retain and release messages being appropriately sent to the relevant object. The same behavior is observed through the 6th line of console's output. Before the control reaches the "testLog" message line, in both the cases, the TestObject created in the "run" method is kept alive up to that point. Then why dealloc it pre-maturely?

This is what I want to happen during compilation while ARC works its magic-

-(void)run{

....

TestObject *obj= [[TestObject alloc] init];

testObj= obj;

[testObj retain]; //ARC's work. By now, retainCount ups to 2.

obj= nil;

...

...

}

What is happening here, behind the scenes, that I'm missing?

Accepted Reply

There are two TestObject instances created. The first is in main(), and you call the run method on it. That method then creates a second instance and stores a reference in its testObj property. That second instance has its own testObj property which is nil of course, since you don’t call the run() method on the second instance. So when you call testLog() on the second instance it reports nil as it should.

Replies

Why do you care? And why even write anything like that to being with? If you want to understand how something works, it is best to start with something sane.

There are two TestObject instances created. The first is in main(), and you call the run method on it. That method then creates a second instance and stores a reference in its testObj property. That second instance has its own testObj property which is nil of course, since you don’t call the run() method on the second instance. So when you call testLog() on the second instance it reports nil as it should.