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 <Foundation/Foundation.h>

@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?

Relax, ARC is working as it should :slight_smile:

-(void)testLog{
    NSLog (@"test successful with %@", testObj);
}

A close inspection of the testLog method reveals that testObj argument to NSLog is a property of the object receiving the testLog message. But that property was not initialised before the message was sent.

-(void)run{
    TestObject *obj = [[TestObject alloc] init];
    testObj = obj;
    
    obj = nil;
    NSLog (@"testObj - %@", testObj);
    
    // ---> testObj.testObj is nil here!
    assert (testObj.testObj == nil);
    [testObj testLog];
    ...

Run the following code to see that’s indeed the case.

//
//  main.m
//  ObjCARC
//
//  Created by Pretty Function on 5/6/19.
//  Copyright © 2019 examples. All rights reserved.
//
#import <Foundation/Foundation.h>

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

@implementation TestObject
@synthesize testObj;

-(void)run{
    NSLog (@"%s: running", __PRETTY_FUNCTION__);
    TestObject *obj = [[TestObject alloc] init];
    testObj = obj;
    
    obj = nil;
    NSLog (@"testObj - %@", testObj);
    
    assert (testObj.testObj == nil);
    [testObj testLog];
    NSLog (@"testObj now - %@", testObj);
    NSLog (@"%s: finished.", __PRETTY_FUNCTION__);
}

-(void)testLog{
    NSLog (@"%s: test successful with %@", __PRETTY_FUNCTION__, self);
    assert (self.testObj == nil);
    NSLog (@"%s: test successful with %@", __PRETTY_FUNCTION__, testObj);
    NSLog (@"%s: test successful with %@", __PRETTY_FUNCTION__, self.testObj);
}

-(void)dealloc{
    NSLog (@"%s: %@", __PRETTY_FUNCTION__, self);
}
@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];
    NSLog (@"%s: finished.", __PRETTY_FUNCTION__);
}

You’re so right. The testObj property, which is logged inside the testLog method, is actually an ivar of testObj property which itself is an ivar of the TestObject pointed to by test pointer.
This is how the object graph is supposed to look-


I’m just kicking myself in the foot. This was so obvious. However, the way testObj was used, tricked me.
There is another small query I needed to have addressed. I have used the dealloc method here as well. But it doesn’t get logged when I’m using clang in the terminal, directly.
This is what the output looks like, in terminal-
gcc -framework Foundation -w objcmemmanage.m -o ~/Desktop/Binaries/objcmemmanage && ~/Desktop/Binaries/objcmemmanage

2019-06-05 17:56:28.831 objcmemmanage[4197:134131] running

2019-06-05 17:56:28.832 objcmemmanage[4197:134131] testObj- <TestObject: 0x7fb1d2600b30>

2019-06-05 17:56:28.832 objcmemmanage[4197:134131] test successful with (null)

2019-06-05 17:56:28.832 objcmemmanage[4197:134131] testObj now- <TestObject: 0x7fb1d2600b30>

2019-06-05 17:56:28.832 objcmemmanage[4197:134131] testObj in the end- <TestObject: 0x7fb1d2600b30>

2019-06-05 17:56:28.832 objcmemmanage[4197:134131] test successful with (null)

Clearly, the dealloc method does not get logged. The gcc command above is actually an alias to clang. However I’m getting the dealloc logged in Xcode. Do I need to add a special compiler flag or something?

Thanks a lot man, for the reply. I remember that you used to be a regular on the forums in 2013. Back then I was studying the 3rd ed. of iOS Programming. Your solutions would often help me learn a better way to solve the “after chapter” challanges. After 2015, I stopped coming to the forums because of some personal reasons. Good to hear from you again