iOS Memory Management

Bored 🥑
5 min readAug 16, 2017

--

Auto Reference Counting

Swift uses ARC to track and manage your app’s memory usage. ARC automatically frees up the memory used by class instances when those instances are no longer needed.

How ARC Works

Every time you create a new instance of a class, ARC allocates a chunk of memory to store information about that instance. This memory holds information about the type of the instance, together with the values of any stored properties associated with that instance.

Additionally, when an instance is no longer needed, ARC frees up the memory used by that instance so that the memory can be used for other purposes instead. This ensures that class instances do not take up space in memory when they are no longer needed.

To make sure that instances don’t disappear while they are still needed, ARC tracks how many properties, constants, and variables are currently referring to each class instance. ARC will not deallocate an instance as long as at least one active reference to that instance still exists.

Strong Reference cycles

This can happen if two class instances hold a strong reference to each other, such that each instance keeps the other alive. This is known as a strong reference cycle.

Reference Cycle

You resolve strong reference cycles by defining some of the relationships between classes as weak or unowned references instead of as strong references.

Using Weak Reference

Weak References

A weak reference is a reference that does not keep a strong hold on the instance it refers to, and so does not stop ARC from disposing of the referenced instance. Because a weak reference does not keep a strong hold on the instance it refers to, it’s possible for that instance to be deallocated while the weak reference is still referring to it. Therefore, ARC automatically sets a weak reference to nil when the instance that it refers to is deallocated. And, because weak references need to allow their value to be changed to nil at runtime, they are always declared as variables, rather than constants, of an optional type.

Unowned References

Like a weak reference, an unowned reference does not keep a strong hold on the instance it refers to. Unlike a weak reference, however, an unowned reference is used when the other instance has the same lifetime or a longer lifetime. You indicate an unowned reference by placing the unowned keyword before a property or variable declaration.

An unowned reference is expected to always have a value. As a result, ARC never sets an unowned reference’s value to nil, which means that unowned references are defined using nonoptional types.

Unowned References and Implicitly Unwrapped Optional Properties

However, there is a third scenario, in which both properties should always have a value, and neither property should ever be nil once initialization is complete. In this scenario, it’s useful to combine an unowned property on one class with an implicitly unwrapped optional property on the other class.

Strong Reference Cycles with Closures

For example, if a closure is assigned to the property of a class, and that closure uses instance properties of that same class, you have a reference cycle. In other words, the object holds a reference to the closure via a stored property; the closure holds a reference to the object via the captured value of self.

You resolve a strong reference cycle between a closure and a class instance by defining a capture list as part of the closure’s definition. A capture list defines the rules to use when capturing one or more reference types within the closure’s body. As with strong reference cycles between two class instances, you declare each captured reference to be a weak or unowned reference rather than a strong reference. The appropriate choice of weak or unowned depends on the relationships between the different parts of your code.

Capture lists come in handy for defining a weak or unowned relationship between objects used in a closure. In this case, unowned is a good fit since the closure could not exist while the instance of CarrierSubscription has gone away.

Define a capture in a closure as an unowned reference when the closure and the instance it captures will always refer to each other, and will always be deallocated at the same time.

Conversely, define a capture as a weak reference when the captured reference may become nil at some point in the future. Weak references are always of an optional type, and automatically become nil when the instance they reference is deallocated. This enables you to check for their existence within the closure’s body.

Cycles with Value Types and Reference Types

Swift types can be categorized as reference types, like classes, and value types, like structures or enumerations. The major difference is that value types are copied when they are passed around, whereas reference types share a single copy of referenced information.

Does this mean that you can’t have cycles with value types? Yes: everything is copied with value types, and thus no cyclical relationships can exist since no real references are created. You need at least two references to make a cycle.

Obj-C Avoid Strong Reference Cycles when Capturing self

  • If you need to capture self in a block, such as when defining a callback block, it’s important to consider the memory management implications.
  • Blocks maintain strong references to any captured objects, including self, which means that it’s easy to end up with a strong reference cycle if, for example, an object maintains a copy property for a block that captures self:

There are two main kinds of problem that result from incorrect memory management:

Freeing or overwriting data that is still in use

This causes memory corruption, and typically results in your application crashing, or worse, corrupted user data.

Not freeing data that is no longer in use causes memory leaks

A memory leak is where allocated memory is not freed, even though it is never used again. Leaks cause your application to use ever-increasing amounts of memory, which in turn may result in poor system performance or your application being terminated.

Using Autorelease Pool Blocks

Autorelease pool blocks provide a mechanism whereby you can relinquish ownership of an object, but avoid the possibility of it being deallocated immediately (such as when you return an object from a method). Typically, you don’t need to create your own autorelease pool blocks, but there are some situations in which either you must or it is beneficial to do so

At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block.

--

--