4
votes

I have a problem understanding the Objective-C and the ARC.

As I understood the strong pointers will be dealloced automatically for you, so you don't have to think about it (dealloced in dealloc method, or after the last time the object was used ?).

So I wrote a little app, with 2 viewControllers and a NavigationController, which enters one view and then goes back.

The dealloc method was called, but the property, which I set at viewDidLoad method, wasn't deallocated, it is still pointing to some object.

Code: The first viewController has a button, which by pressing it, performs a segue to another viewController. No code there.

SecondViewController.m

@interface SecondViewController () 
@property (nonatomic, strong) NSString *myString;
@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"%@", _myString);
    _myString = @"it works";
}

- (void)dealloc {
    NSLog(@"%@", _myString);
    // and now it is deallocating the _myString property ???
}

@end

Then, I tried to do another thing.

The idea was to create a weak pointer, which points to the same memory address, as the strong pointer. I though, that the weak pointer should be nil in any case.

  1. Since the dealloc method is called, all weak pointers should be niled

  2. Since the strong pointer was used only in viewDidLoad, it should be deallocated way before the dealloc method.

The problem is, it is not deallocated. Why ?

Code for secondViewController:

@interface SecondViewController ()
@property (nonatomic, strong) NSString *myString;
@property (nonatomic, weak) NSString *test;
@end

@implementation SecondViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSLog(@"%@", _myString);
    _myString = @"it works";
    _test = _myString;
}

- (void)dealloc
{
    NSLog(@"%@", _test);
}

@end
2
Note that testing behavior like this is never going to work. First, a static string like @"it works" will never be deallocated because it is never allocated in the first place; it is a part of the executable image. Secondly, even if the object were deallocated, messaging a deallocated object exhibits undefined behavior and is not guaranteed to cause a crash or other symptom.bbum
What do you mean, it is never allocated ? Is it then like a "global" variable with the lifetime of the app or ...? Isn't _myString = @"it works" same as _myString = [[[NSString alloc] initWithString:@"it works"] autorelease] or something like this ? Since whenever I try to write something like this, Xcode shows me a WARNING to write _myString = @"it works"denis631
@denis631 Yes. But it's different from the way I built a string using stringWithFormat: in my answer.nhgrif
@denis631 A string created by @"..." is actually created at compile time and is a part of the app's executable. It is mapped into memory along with the rest of the app's data (and code) and, thus, it isn't part of the allocation heap of the app. Said objects don't do anything when released or retained and they are never deallocated because there is no allocation in the first place. Using NSString (or other framework classes) is oft confusing for this, and other (see tagged pointers), reasons. Always use your own subclass of NSObject when doing these kinds of learning exercises!bbum

2 Answers

3
votes

Deallocation of the properties happens at the end of the dealloc method. If you overwrite the dealloc method, the properties won't yet be deallocated inside that method.


You could test this by creating a weak property in your first view controller, assign the strong property of the second view controller, then log the value of it when the application returns to the first view controller.

3
votes

The simplest way to illustrate weak references is with the following example...

Given the following two classes:

@interface ObjectWithStrongRef : NSObject

@property (strong) NSString *ref;

@end

@interface ObjectWithWeakRef : NSObject

@property (weak) NSString *ref;

@end

We will create an instance of ObjectWithWeakRef with a scope larger than that of ObjectWithStrongRef, assign the latter's ref property a value, then have the former's ref point to this same object, then we will check ref in both scopes.

int main(int argc, const char * argv[]) {
    ObjectWithWeakRef *weak = [[ObjectWithWeakRef alloc] init];

    @autoreleasepool {
        ObjectWithStrongRef *strong = [[ObjectWithStrongRef alloc] init];
        strong.ref = [NSString stringWithFormat:@"Hello %@", @"World"];
        weak.ref = strong.ref;

        NSLog(@"Weak.Ref = %@", weak.ref);
    }

    NSLog(@"Weak.Ref = %@", weak.ref);
}

Note, that we can't simply assign ref to a literal string. Objective-C tends to keep these around in memory so it can do some memory optimizations, but when we use stringWithFormat:, it'll create an autoreleasing string.

At the first NSLog statement, strong.ref maintains a strong reference to the string object, so when we log weak.ref, the object is not yet deallocated, so it correctly logs "Hello World".

Between the first and second NSLog call, we've exited the @autoreleasepool, within which the strong object was scoped (if we put an NSLog message in ObjectWithStrongRef's dealloc, we'd see it called here). Because strong has deallocated as we exit the @autoreleasepool, there are no longer any strong references to the string object we have references to--we only have weak's weak reference to the memory, so the string object also deallocates (just after strong has deallocated).

So in the second NSLog call, we'll see Weak.Ref = (null) printed.