Maxim Maxim - 2 months ago 14
Objective-C Question

ARC doesn't work properly in Xcode with Objective-C

I have a dilemma with strong and weak references in Xcode with Obj-C.
When I create two objects with cross-references to each other inside, all weak:

Mouse *mouse = [[Mouse alloc] initWithComputer:nil];
Computer *computer = [[Computer alloc] initWithMouse:mouse];

NSLog(@"%@", computer.mouse); //prints legit address

mouse = nil; //destroy the ONLY strong reference to Mouse

NSLog(@"%@", computer.mouse); //still prints(holds) the same legit address


Before mouse = nil;

enter image description here

After mouse = nil;

enter image description here

After "destroying" the ONLY strong reference to Mouse class mouse, an instance of Mouse computer.mouse still holds the same memory address when it should be deallocated.

Absolutely the same code only in SWIFT in Xcode works correctly and deallocates the memory being held by computer.mouse making it nil

Is anything wrong with my Obj-c code? My Xcode is up to date, though with the previous version I had no luck either in Obj-c. I would appreciate any help.

Here are my classes:

Computer.h

#import <Foundation/Foundation.h>
@class Mouse;

@interface Computer : NSObject

@property (nonatomic, weak) Mouse *mouse;

- (instancetype) initWithMouse: (Mouse *) userPutMouse ;

@end


Computer.m

#import "Computer.h"
#import "Mouse.h"

@implementation Computer

- (instancetype) initWithMouse: (Mouse *) userPutMouse {

self = [super init];

if (self) {

self.mouse = userPutMouse;
self.mouse.computer = self;

}

return self;
}

@end


Mouse.h

#import <Foundation/Foundation.h>
@class Computer;

@interface Mouse : NSObject

@property (nonatomic, weak) Computer *computer;

- (instancetype) initWithComputer: (Computer *) userPutComputer;

@end


Mouse.m

#import "Mouse.h"
#import "Computer.h"

@implementation Mouse

- (instancetype) initWithComputer: (Computer *) userPutComputer {

self = [super init];

if (self ) {

if (userPutComputer) {
self.computer = userPutComputer;
}

}

return self;
}

@end

Answer

Your getters, being automatically synthesised, are placing the objects they return into the autorelease pool. Therefore your assignment of mouse to nil does not end the final owning reference. Never assume you are the only person with owning references to anything, only ensure that you follow appropriate behaviour.

For empirical demonstration, try this:

Mouse *mouse;
Computer *computer;

@autoreleasepool {

    mouse = [[Mouse alloc] initWithComputer:nil];
    computer = [[Computer alloc] initWithMouse:mouse];

    NSLog(@"%@", computer.mouse);  //prints legit address

    mouse = nil;  //destroy **my** ONLY strong reference to Mouse

}

NSLog(@"%@", computer.mouse); //prints (null)

... but also never try to diagnose whether proper ownership is happening empirically. That's why the retainCount property is no longer available.

EDIT: to expand: the traditional behaviour expected of a getter is to return a non-owning reference that is nevertheless guaranteed to live for at least as long as the current call stack. This is so that, in pre-ARC days, you could use getters directly for supplying properties as arguments (in which case, don't burden the caller with memory management) or for getting something the caller can hold on to transiently, even if the original owner is deallocated in the meantime (i.e. the classic autorelease pool usage of making things act a bit like they're just on the stack). And then ARC just implements the old-fashioned rules but automatically, for full interoperability.