Some Subtle Behaviors of ARC

Lightning

Automatic Reference Counting (ARC) is a method of memory management for Objective-C.

In this post, I’m going to explore a couple of subtle behaviors of ARC that I haven’t seen explicitly documented elsewhere. Once you’ve seen these behaviors, they’re obvious. But if you are new to ARC, there may be a couple of surprises for you. Let’s look at three short snippets of code that demonstrate these behaviors.

First, let’s set up an object with some properties:

@property (strong) NSString *strongDirect;
@property (strong) NSString *strongSetter;
@property (weak) NSString *weakPointerConvenience;
@property (weak) NSString *weakPointerAllocAndInit;
@property (weak) NSString *weakPointerInAutoReleasePoolBlock;

Next, let’s write a short method to log the objects referred to by each property:

-(void)logProperties:(NSString *)contextDescription {
    NSLog(@"\n\n** %@ **\n\n",contextDescription);
    NSLog(@"Direct Assign Strong Pointer:\n   %@", 
        self.strongDirect);
    NSLog(@"Setter Assign Strong Pointer:\n   %@\n\n", 
        self.strongSetter);
    NSLog(@"Weak Pointer from Convenience Constructor:\n   %@",   
        self.weakPointerConvenience);
    NSLog(@"Weak Pointer from Alloc & Init:\n   %@", 
        self.weakPointerAllocAndInit);
    NSLog(@"Weak Pointer (Convenience in @autoreleasepool block):\n   %@\n\n",    
        self.weakPointerInAutoReleasePoolBlock);
}

Finally, let’s write a method to assign some objects to our pointers and call our log method. Note that our log method will be called three times. Take a moment to think about what you expect it to print out.

-(void)testARCBehaviors
{
    _strongDirect =
        [NSString stringWithFormat:@"DAS %@", [NSDate date]];
    self.strongSetter =
        [NSString stringWithFormat:@"SAS %@", [NSDate date]];

    self.weakPointerConvenience =
        [NSString stringWithFormat:@"WACC %@", [NSDate date]];
    self.weakPointerAllocAndInit =
        [[NSString alloc] initWithFormat:@"WAAI %@", [NSDate date]];

    @autoreleasepool {
        self.weakPointerInAutoReleasePoolBlock =
            [NSString stringWithFormat:@"WACCAP at %@", [NSDate date]];

        [self logProperties:@"Within an @autoreleasepool block"];
    }

    [self logProperties:@"After the end of the @autorealeasepool block"];

    [self performSelector:@selector(logProperties:)
               withObject:@"After a 0.01 second delay"
               afterDelay:0.01];
}

Now that you’ve had a moment to think about what you expect this code to print out, let’s take a look at what we see in the debugging console. I’ve taken out the line headers to make the output a little easier to read.

** Within an @autoreleasepool block **

Direct Assign Strong Pointer:
   DAS 2013-06-15 21:42:51 +0000
Setter Assign Strong Pointer:
   SAS 2013-06-15 21:42:51 +0000

Weak Pointer from Convenience Constructor:
   WACC 2013-06-15 21:42:51 +0000
Weak Pointer from Alloc & Init:
   (null)
Weak Pointer (Convenience in @autoreleasepool block):
   WACCAP at 2013-06-15 21:42:51 +0000

** After the end of the @autorealeasepool block **

Direct Assign Strong Pointer:
   DAS 2013-06-15 21:42:51 +0000
Setter Assign Strong Pointer:
   SAS 2013-06-15 21:42:51 +0000

Weak Pointer from Convenience Constructor:
   WACC 2013-06-15 21:42:51 +0000
Weak Pointer from Alloc & Init:
   (null)
Weak Pointer (Convenience in @autoreleasepool block):
   (null)

** After a 0.01 second delay **

Direct Assign Strong Pointer:
   DAS 2013-06-15 21:42:51 +0000
Setter Assign Strong Pointer:
   SAS 2013-06-15 21:42:51 +0000

Weak Pointer from Convenience Constructor:
   (null)
Weak Pointer from Alloc & Init:
   (null)
Weak Pointer (Convenience in @autoreleasepool block):
   (null)

So let’s review what we’ve just seen, and consider how it might affect our coding.

  • Strong pointers are really strong. In Manual Reference Counting, if you failed to use a property’s setter when assigning a new object to that property, you could run into serious problems with memory leaks and dangling pointers. In ARC, the object is appropriately retained whether or not you use the object’s setter.
  • Weak pointers are really weak. If you create an object using alloc and init, and then assign that object to to a weak pointer or property without keeping any strong reference to it, the object will be immediately deallocated. It won’t even survive to the end of your current scope.
  • Weak references get interesting when you are working with convenience-constructed objects.  Convenience constructors typically return auto-released objects which will hang around until their enclosing @autoreleasepool block is drained.
    • UIApplication maintains an autorelease pool that will be drained automatically at the completion of your run loop. This typically means that autoreleased objects will hang around until the end of your scope if you assign them to a weak pointer.
    • While convenience-constructed objects referred to by a weak pointer will typically survive until the end of your scope, you shouldn’t count on them lasting much longer. In our example here, they’re gone one hundredth of a second after the method returns.
    • You can also force auto-released objects to be released within your current scope by setting up a local @autoreleasepool block. Operations like data parsing can create a lot of memory-consuming auto-released objects. Thus, it may be a good idea to set up a local @autoreleasepool block if you are working with lots of objects created by convenience constructors.
  • As you can see from our example, an @autoreleasepool block only releases the auto-released objects created within its scope.  You can use this to release some auto-released objects that are no longer needed without deallocating other auto-released objects that you still need.

One final note: if you enter the code exactly as you see it above, Xcode will give you a warning when you assign an object created with alloc and init to a weak property.  However, it will not give you a warning when you do this with an object returned by a convenience constructor.

The image at the top of this post is based on “Double Lightning in Glyfada-Athens” by NikoSilver, a public domain photo from the Wikimedia Commons.

One thought on “Some Subtle Behaviors of ARC

  1. Admin Post author

    Postscript: The example above runs as shown, but it doesn’t prove that the direct assign to a strong pointer isn’t just a zombie object that has already been released and deallocated. I did a test to verify this outside of the code shown here, and the direct assign to the strong pointer is a happily retained object – it is not a zombie.

Comments are closed.