Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: tech-talks-2011-12
$eventId
ID of event: tech-talks
$eventContentId
ID of session without event part: 2011-12
$eventShortId
Shortened ID of event: tech-talks
$year
Year of session: 2011
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2012] [Tech Talk World Tour] Ta...

iOS 5 Tech Talk World Tour #12

Taking Advantage of Automatic Reference Counting

2011 • 30:29

Xcode 4.2 introduces a powerful new technology called Automatic Reference Counting. Learn how the new Apple LLVM compiler can take care of memory management for you. Understand how ARC can dramatically simplify your development process by helping you write less code while reducing memory leaks and crashes. Discover how Xcode makes it easy to move your existing projects to ARC.

Speaker: Paul Marcos

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Hi, I'm Paul Marcos, Application Services Evangelist at Apple. When you write iOS and Mac OS X applications, you do so using the Objective-C programming language. I love using Objective-C. It's a simple, elegant language that's easy to use, and you can write great applications with it. And we've made it even better by adding a really cool new technology called Automatic Reference Counting.

Historically, Objective-C has used a manual reference counting mechanism for managing the lifetime of objects. Unlike an automatic garbage collection system, you have to be careful to avoid mistakes that can lead to memory leaks or application crashes. Unfortunately, these are common problems, especially for programmers new to Objective-C. Thankfully, Automatic Reference Counting, or ARC, eliminates the manual and error-prone steps involved in managing reference-counted objects, freeing you to focus on the design of your object relationships and the functionality of your application. Whether you're new to Objective-C or a veteran coder, there's plenty to learn about this powerful new technology and how it will lead to simpler, more robust, and more easily maintained code. Let me show you how this is going to make your life so much better.

So what exactly is ARC? Automatic Reference Counting implements automatic memory management for Objective-C objects and blocks, freeing the programmer from the need to explicitly insert retains and releases. That's the textbook style answer, which, as most textbooks are, is pretty dry. It's actually a lot more interesting than that. So let's see what makes ARC so great.

On iOS, you're responsible for managing the memory yourself. But it's hard. There's a lot of details and you've got to get them all right. It's really easy to make a mistake and then your app crashes or leaks. So are there any alternatives? How about Garbage Collection? Garbage Collection does all that stuff for you automatically and all the complexity just goes away. So it's pretty obvious that Garbage Collection is the better way, right?

Well, let's put these side by side and see how they compare. Developing in a manual memory management environment can be a pain. Garbage collection manages memory for you and makes it a lot easier. On the manual side, you have to write a lot of code that really doesn't add any functionality to your app. With GC, you don't have to do that, so you can focus on writing code that actually does something useful.

And you just need to make a single mistake in the manual world and your app crashes. You typically don't have that problem in a garbage collected environment. So it's pretty clear that garbage collection wins over manual memory management, right? Well, it's not that easy. GC also has some downsides. First, with GC, garbage can build up, as it might take a while for the collector to kick in and clean things up. That's not good, especially if you don't have that much memory available, like on a mobile device.

That's where Manual Reference Counting clearly performs better. As objects are freed, the memory used is immediately available. Second, GC is non-deterministic. You typically don't have control over when the collector kicks in. You don't really know what state your app might be in, nor how long it will take before you're back in control. With manual management, you are in control and the behavior is predictable.

Back on the GC side, because you're not in control, it's possible that collection will be done at a crucial point in time, like when the user is scrolling through a list or zooming a photo. This can lead to visible UI interruptions. With manual management, it's easy to set the priorities right and deliver smooth performance. These are just some of the reasons that GC is not available in iOS. We feel that smooth performance and a stutter-free user experience are what matters to our users. Regardless, out of these two options, neither one is a slam dunk winner.

What we really want is something that looks like this. It's the best of both worlds. Automatic memory management without the performance drawbacks. And that's exactly what we did with ARC. It's still memory management based on reference counting, just like it used to be when you had to do it manually. It's just done automatically for you. So if you don't have to do it, who does it?

We taught our LLVM compiler to do the memory management for you. It magically inserts all the necessary bookkeeping calls for you in exactly the right places. This automatic bookkeeping is done at compile time, which means there's no thread or process at runtime, so no impact on performance. To better understand how this all works, let's look at the following four things. First, we'll look at what impact this has on how you write your code. Then I'll show you how you can migrate an existing project to ARC.

Next, we'll go through some tips and tricks for using ARC. And we'll end with a couple notes about performance. Let's dive in. There's an old adage that goes something like this: "The best line of code is the line you never had to write." That sums up what writing code with ARC is really all about. If you already know Objective-C, it's not so much about learning how to write ARC code. It's about not writing the code in the first place. It's really all about writing less code.

Because the compiler does all this work for you now, there's simply less code for you to write. Let's take a look at three key areas where ARC impacts how you write your code. The first is about our old friends, retain, release, and auto-release. So what's the big change with retain, release, and auto-release? Well, it's very simple. They just go away. You never have to write them again. So what happens to your code?

Under Manual Memory Management, if you allocate an object, then you own it, and it's your responsibility to release it. But under ARC, you still allocate it, and you still own the object, but the compiler keeps track of things and releases it for you as soon as you're done with it. So with ARC, the release just goes away. If you're already familiar with Objective-C and how reference counting works, this just feels really weird. That code just looks wrong. But with ARC, it's really not. You just have to forget about the need to call release.

Of course, you can still use the convenience class methods to create your objects. In this example with a local variable, nothing changes using ARC. But what about using convenience methods with instance variables? Here's part of a simple stack implementation. If you did this under manual reference counting, you'd be in for a rude surprise because your app would end up crashing.

The problem is the array created here is auto-released, and because it's not been retained, it will be deallocated after the init method is done. Any subsequent uses of that instance variable will likely crash. There's a couple ways you could address this. First, you could alloc and init the array.

Now you own the array, and it won't be deallocated. Well, now your app won't crash anymore, but you've traded a crash for a leak, because the array is never being released. So this is no good either. Now you would need to balance out the retain with a release in dealloc.

Great. Now you're good under Manual Reference Counting. And that's perhaps the simplest example, but it's illustrative of how easy it is to slip up and forget to add that release, or to use an object that has already been deallocated. With ARC, the compiler has got your back and remembers all of this for you.

It'll keep track of your instance variables automatically, so you don't have to write the release in DL. Under ARC, we can just take that out. In fact, you don't even need to write the super DLK anymore because the compiler will always automatically invoke your superclass's DLK method if you implement one.

So this is totally fine under ARC. Again, this looks really wrong if you're familiar with manual memory management, but it's actually correct with ARC. And you can even just use the convenience methods in this case. Under manual reference counting, this would crash, but with ARC, the compiler does the right thing.

Let's consider another aspect of this stack class to see a different example. This pop method removes the last object from the stack and returns it. That's pretty simple, right? Well, under manual reference counting, this method would likely not work and may actually crash. Why? Imagine if that last object you wanted to return was only being retained by the array itself. The act of removing it from the array would decrement the retain count and the object would then be deallocated. So this code could very well return a deallocated object, and your caller won't be happy about that.

So you might simply add a call to retain the object so that it's not deallocated anymore before you return it. But now you're returning a retained object. This would be a leak and violates the naming conventions used in Objective-C. So you would need to auto-release it when you return the object.

Now you're good under Manual Reference Counting. But with ARC, you don't have to remember this whole dance at all. The compiler does all this bookkeeping for you, and these pitfalls just go away. This actually takes us back to where we started with that simple implementation. And that's what you'd write using ARC. You write code in a way that just feels natural.

Let's look at the object graph to better understand what's going on with ARC. Somewhere in your code, you have a reference to the stack. And the stack has a reference to the array. And the array holds references to some objects. These are strong references. The first thing happening in the pop method is you create a reference to the last object in the array.

The compiler will make sure that this reference is actually a strong reference. And as you can see, when you remove the last object from the array, it'll remove the strong reference the array had to the object. But since the compiler created a strong reference from X, the object doesn't go away. But what happens when this code returns X to the caller?

Well, the compiler is smart enough to understand that you want to pass this reference back to whatever code is accepting the return. So it makes sure even when X goes out of scope, the reference is passed over to the caller and will continue tracking it there. Once the object is ultimately no longer needed, then it will be released automatically.

So ARC does some wonderful things with managing Objective-C objects. But let's change gears slightly and talk about blocks for a minute. In order to manage blocks correctly in the manual world, you sometimes have to copy them since they're created on the stack. For example, if you wanted to return a block, you would have to copy it first, which would move it from the stack to the heap, and then you would have to auto-release the copied block so that it would be cleaned up correctly.

Thankfully, under ARC, blocks just work right. You don't even have to copy them before you return them. The management all happens automatically. So the impact of ARC on how you write code is that you just write what feels natural. You don't have to call retain, release, or auto-release anymore.

In fact, you're not allowed to. With ARC enabled, these will generate compile errors. You don't need to implement D-ALEC anymore either in order to clean up your object references. However, if you have malloced memory, things like file descriptors or non-objective C objects, you would still need to do that cleanup in D-ALEC.

You can choose to use alloc init or convenience methods to create objects. It doesn't matter which one you use. Block references work automatically. And finally, ARC lets you really focus on what your object graph should look like, not in policing the existence of those objects. So think about relationships between your objects and not about their reference counts.

Okay, the second thing I want to cover is another old friend, NS Auto Release Pool. With ARC, the number of times you have to explicitly use an auto-release pool has been reduced greatly. ARC generally doesn't require you to deal with auto-release pools anymore, unless perhaps you're dealing with a framework generating lots of auto-released objects. In this case, you might want to use one in order to keep a lid on memory usage.

The change here is that we're pulling it directly into the language with a direct syntax for defining a block of code wrapped with an auto-release pool. This is much easier, much more natural, and a lot faster than using the NS Auto-Release Pool class. Let's take a look at an example.

Here we have a while loop with an auto-release pool around it which will periodically get drained. With ARC, the need to aggressively drain auto-release pools is greatly reduced. The compiler and the runtime will work together to make the management much more efficient. In fact, in a lot of cases, retained and auto-released objects don't even end up in the pool at all. However, if you do need to wrap code with an auto-release pool explicitly, you can do so with this simple syntax, and that lets the compiler understand what's going on.

The third big change is about managing your object graph. Specifically, about the issue of retain cycles. ARC will also help solve this problem. Let's use an example of a hierarchy of objects. At the top, we have a collection object, which might have a parent node, and that parent node has a child node. For convenience, our child node wants to have a reference back to its parent.

If this were implemented with retained references, this would create a circular reference between the parent and the child. So what's the problem with that? Well, if the parent is removed from the collection, its retain count would drop, and it would normally go away. But it doesn't because the child still has a strong reference to the parent. So both objects would be leaked.

Since they're mutually referring to each other, neither retain count will drop to zero and they'll never go away. In this simple example, you might say, well, that's not a big deal since it's just two objects. But usually objects will have references to other objects. And now this whole tree of objects is being leaked.

So a common solution to this problem is to change the reference from the child to the parent to be a weak reference. This works fine, but introduces the burden of making sure that that weak reference gets cleared out correctly and doesn't end up as a dangling pointer to a deallocated object.

While ARC doesn't directly solve the retain cycle problem, it introduces a new feature that does. It's called zeroing weak references. Zeroing weak references are similar to assigned properties. They do not increase the retain count of the object they point to, which means they do not form a retain cycle.

Additionally, zeroing weak references are safer than using assigned properties because they automatically get set to nil if the pointed to object is deallocated. This prevents the reference from turning into a dangling pointer. When combined with the automatic management provided by ARC, zeroing weak pointers will help you manage your retain cycles efficiently.

So how do you use these zeroing weak references? Two new property attributes have been introduced with ARC. First, the retain attribute changes to strong. Functionally, nothing changes here. Strong properties work exactly like retain properties used to. Next, assign becomes weak. Weak properties are these new zeroing weak references. Let's look at perhaps the most stereotypical example where we can benefit from zeroing weak references. Delegates are an established pattern of a reference to an object that's not retained.

These are commonly specified as properties using a sign, meaning that they don't get retained. If you've ever had to debug a crashing bug that turned out to be a stale delegate reference, then this change is for you. Now just set your delegate property to weak, and when your delegate gets deallocated, your reference will be cleared out automatically.

Let's summarize what changes when you write code using ARC. First, retain, release, and auto-release go away. You can purge them from your memory. NS-AutoReleasePool becomes @AutoReleasePool, with direct support in the language syntax. Retain properties become strong, and assign properties become weak. Next up, let's look at how you can migrate your existing projects to ARC. ARC is available in iOS 5 and in Lion. But we want you to use ARC in your projects starting today, which is why we made it work back in iOS 4 and Snow Leopard as well. However, zeroing weak references are only available in iOS 5.

Keep in mind that ARC is really just automating retain and release for you. Therefore, it's completely compatible with existing retain release code. And you can opt in or opt out of ARC on a file by file basis by using these compiler flags. But you really should switch to using ARC today. For new projects, starting with Xcode 4.2, ARC is now enabled by default. You can certainly enable ARC in existing projects as well.

For that, you need to make sure you're using the Xcode 4.2. You can also make sure you're using the LLVM3 compiler and that you set the Objective-C automatic reference counting build setting to Yes. And then you just need to go through all your code and make all those changes that we talked about. Or you can use the ARC migration tool.

It's supported in an Xcode 4.2. You can find it in Xcode's Edit menu under Refactor Convert to Objective-C ARC. And it applies the changes for you to your code. It'll remove all the calls to retain, release, and auto-release. It'll replace NSAutoReleasePool with the new @AutoReleasePool syntax. And it changes assigned properties to weak, switching them to zeroing weak references. The ARC migration tool works in three stages.

First, you select the target to convert, and it will check to make sure your code is using existing best practices. If it finds places where you aren't adhering to conventions, or if it finds really tricky cases that the tool can't convert automatically, it calls them to your attention.

The next step is to apply the changes to your code. But don't worry, it won't apply any of the changes just yet. The last step is to present the diffs to you for review. You can see exactly what changed and choose which files you want to have converted.

For those of you who are still supporting iOS 4, zeroing weak references aren't available, so the migration tool will not change assigned properties to weak. Instead, it will change them to unsafe underbar unretained. Which gives you the same unsafe behavior that a sign did, so beware of dangling pointers.

Now let's take a look at some tips and tricks for situations you might run into when writing ARC code. First up are singletons. Some developers implement singletons by writing their own retain and release methods. Under ARC, you can't call or even implement retain and release anymore. Instead, you could use a shared instance pattern and make sure your singleton objects are only initialized once.

You can have a class method that hands out a shared instance of this object. This shared instance is stored in a static variable, and if it hasn't been created yet, it'll get created. If you want to make this thread safe, you could also use DispatchOnce. DispatchOnce will make sure the block you pass to it executes only once.

Or another way to achieve a similar effect is to simply use the fact that classes are singletons. For example, let's say you have multiple network requests going on and want to display the network activity indicator as long as at least one of these requests is still alive. A simple static counter might be all you need.

Ownership qualifiers allow you to tell the compiler how it should do memory management for Objective-C types. Underbar underbar strong is the default and will keep an object alive. Underbar underbar weak will keep a safe reference as long as the object exists. Here's a fun example to see ARC in action. If you were to declare a local variable as weak and then never assign the object it points at to something with a strong reference, it can go away immediately after it's created.

So in this example, it will actually print null for the string because S was only a weak reference, so the object got released and S automatically got set to nil. There's also __unsafe_unretained, which will be a traditional unsafe pointer reference that is not automatically zeroed out. And lastly, there's __autoreleasing, which can be used when passing objects by reference.

Blocks can present some difficulties with retain cycles. For example, in Manual Reference Counting, this code has the effect of not retaining X. In ARC, this defaults to retaining X, just like all other objects because strong is the default. To get the manual reference counting behavior under ARC, you could use the unsafe unretained, but as the name implies, having a non-retained variable is dangerous and is therefore discouraged. Two better options would be either to use underbar underbar weak if you don't need to support iOS 4 or Snow Leopard, or to set the block value to nil in order to break the retain cycle.

Here's an advanced tip that might not apply to you, but if it does, it's an important one. This can be pretty confusing, but hang in there and you'll get the gist of it. ARC automates the management of Objective-C objects and blocks. If you're using core foundation and rely on the toll-free bridging between Objective-C and CF, then you have to give the compiler a hand so that it knows what to do. The first case is simply to do a cast without implying any change in ownership between CF and Objective-C. To do this, you use the underbar underbar bridge keyword. This will just do a straight pointer cast and nothing changes with the management of that memory.

However, there are times where you want to move an object from CF to Objective-C and then use Objective-C conventions for managing the object's lifetime. For example, let's say you have an object in CF that was created with one of the various CF create functions. Ordinarily, you might just cast this to an Objective-C object and then later call release or auto-release on it.

But with ARC, you can't call release or auto-release, so you're kind of stuck. To satisfy the obligation, you can use the CF bridging release function, which will fulfill your duty on the CF side and release the object. And then at that point, ARC will take over the ownership of that object.

Conversely, you might want to take an Objective-C object that's being managed by ARC and cast it over to a CF object, that object managed under CF memory management rules. To do this, you would use the corresponding CFBridgingRetain function, which will increment the retain count of the object by one and remove it from being managed by ARC.

At this point, the returned CF object will need to have CFRelease called on it in order to satisfy the memory management obligations. The takeaway point here is if you're moving objects between CF and Objective C, you have to make sure you abide by the rules on the CF side when moving objects back and forth.

Our next tip has to do with switch statements. Technically, C allows you to define a variable under a case label in a switch statement. This makes it hard for the compiler to reason about what your code is doing and when the object needs to be released. LLVM under ARC will make you aware of this problem.

A simple solution is to help the compiler out by wrapping the body of a case statement with curly braces. This makes it clear what the expected scope of the local variable is. Under ARC, you also can't put object references in C structures. Instead of using a simple C structure here, consider a full-fledged Objective-C object.

If you end up mixing and matching ARC and non-ARC code, you need to be careful with method names. ARC expects the usual Cocoa naming conventions to be followed. This is especially important for methods that transfer ownership of objects. For example, if this serial method were compiled with ARC and then called by non-ARC code, the compiler will return an auto-released object.

And this new serial method would return a retained object because the method name begins with "new." This is also why you can't have properties that begin with "new." The important point here is that if you're mixing and matching worlds, you need to be careful and play by the rules of the conventions on the non-ARC side, because that's what the compiler will do on the ARC side.

Lastly, let's talk a little bit about performance. We've put a lot of effort into making ARC as efficient as possible, and we've tuned up the Objective-C runtime for ARC. First, the retain-release implementation on NSObject now only takes 40% of the CPU cycles as it did before, which is a major speed-up. Secondly, the new at-autorelease-pool syntax is much faster than the old NSAutorelease-pool class.

It's not actually allocating an object anymore, and it now only takes about 20% of the CPU cycles than it did with NSAutorelease-pool. Third, it's common practice in Cocoa to return auto-released objects from getter methods. This frequently means the returned object stays around longer than it's needed, until the current auto-release-pool goes away.

Since the compiler and runtime were co-designed for ARC, this enables a number of really amazing optimizations. The runtime and compiler know when you are in an ARC method, returning an object back to ARC code. In this case, the runtime avoids putting that object in the auto-release-pool at all. So this is a nice performance win.

The cost of doing these auto-released getters when the optimization kicks in is now only 5% of the CPU cycles that it used to be. And additionally, all these objects are managed by ARC, so they go away as soon as they're no longer needed, which also reduces the high watermark for memory usage in your apps.

Well, that's a whole lot of information about ARC. To recap, now you know what ARC is, how it makes your apps much more stable by reducing leaks and eliminating crashes. You've seen how writing ARC code means writing less code, leaving you with more natural and more maintainable code. You've seen how you can migrate your apps to ARC with the ARC Migration Tool. And we've talked about a few tips and tricks, and have seen some data about ARC's amazing performance improvements.

Automatic reference counting is one of the most exciting things to happen in Objective-C, and I hope you embrace it starting today. By doing so, you'll be well along the path towards more robust code that doesn't leak and crashes a lot less. If you're already familiar with manual reference counting, it might feel a little funny at first, but you'll quickly shed the burdens of having to worry about retain, release, and auto-release. If you're just starting off with Objective-C, then definitely stick with ARC and you'll be in great shape. Either way, I'm positive you'll be very happy with the results ARC will give you.