4

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
denis631
  • 1,765
  • 3
  • 17
  • 38
  • 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 Mar 25 '15 at 20:49
  • 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 Mar 25 '15 at 21:09
  • 1
    @denis631 Yes. But it's different from the way I built a string using `stringWithFormat:` in my answer. – nhgrif Mar 25 '15 at 21:14
  • 2
    @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 Mar 25 '15 at 22:38

2 Answers2

3

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.

Lord Zsolt
  • 6,492
  • 9
  • 46
  • 76
  • 2
    For autoreleased objects, it can also happen *after the end of `dealloc`*, when the auto-release pool drains. This is typically at each iteration of the run loop. – Aaron Brager Mar 25 '15 at 20:28
3

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.

nhgrif
  • 61,578
  • 25
  • 134
  • 173
  • 1
    Note that relying on the exact timing of nullification of a weak reference is exceedingly fragile. All that is guaranteed is that the weak ref will be zeroed before the referenced object is destroyed. When that destruction happens may vary for subtle reasons. For example, move the declaration of `strong` (but not the assignment) outside of the `@autoreleasepool` block and the output will be very different. – bbum Mar 25 '15 at 20:53
  • 1
    @bbum Right. I wouldn't recommend relying on the timing of a `weak` reference zeroing, but you should never get an exception sending a message to a `weak` reference, correct? Either the object still exists (even if it's about to dealloc) or `weak` is zeroed out, and sending a message to it will simply do nothing and return `nil`. – nhgrif Mar 25 '15 at 20:57