Codementor Events

Memory Managment - iOS

Published Jun 25, 2018Last updated Jun 26, 2018

Memory Directives (Objective-C)

You may have typed many lines that look something like this.

@property (nonatomic, strong) NSMutableArray *items`;

This is a pretty standard property declaration you’d find in a .h file or at the top of a .m implementation file. Now, nonatomic specifies how different processes in a multi-threaded app can interact with a property. If you want to make sure your property is thread safe, you would use the attribute atomic instead of nonatomic. Here’s more details about multi-threading. Strong, on the other hand, is a memory directive.

strong (memory-directive) — determines when we increase an object’s reference count and when memory can be released.

Strong is just one of the options we have, but it’s the most common one we use. It’s also the same as retain, which we would see in pre-ARC property declarations.

Simply put, strong means that the reference count will be increased and the reference to it will be maintained throughout the life of the object. When the count reaches zero, the memory can be released.

Weak means that we are pointing to an object, but not increasing its reference count. It is often used when creating a parent-child relationship. The parent has a strong reference to the child, but the child only has a weak reference to the parent.

read-only means that you can set the property initially, but then it cannot be changed. This could come in handy for all sorts of stuff you don’t want to accidentally changed.

copy makes a copy of the object to be referenced. It then points to the copy instead and sets its reference count to 1. Using copy means that we are copying the value of the object when it is created. This prevents its value from changing if the value of an object it’s pointed to changes.

Retain Cycle ( Objective-C)

This happens when object holds a strong reference to each other. Simply put, a retain cycle is where x is holding onto y, and y is holding onto x, but no other objects care about either x or y anymore. But you still can’t release them, because they’re holding onto each other so they both leak. A retain cycle involving something small like some strings or numbers may not cause noticeable performance problems, but if you use the same code to handle heavier items, like audio clips or big objects, it might crash. A lot of time this can happen with a block, where the block retains the thing that created the block, and never gets released, so that the thing that created the block never gets released either. This is a problem that is tough to solve.

Swift ARC

Like Objective C, Swift makes use of automatic reference counting (ARC). Many of the considerations you need to make are similar to Objective-C, though overall you actually have even less to worry about.

The first thing you’ll notice as different in Swift memory management is that we aren’t explicitly declaring memory directives like ‘strong’ and ‘copy’, like they would be in Objective-C property declarations. In Swift, all properties of a class are assumed to be strong, so you can just simply declare a property without writing ‘strong’. Of course ‘Swift’, like Objective-C, isn’t immune to retain cycles. So often, you will have to explicitly declare a weak reference, for instance, between a parent and a child. To do this, you do need to use the keyword, weak.

Retain Cycle (Swift)

Like I mentioned above, if there were strong references in both directions, you can’t release both objects and there will be memory leak.

Let’s think about the situation where one of the objects could never be nil. For example, let’s say we have a user and a user avatar. In this scenario, a user might choose not to have an avatar, and instead just display some placeholder image. However on the flip side, a user avatar must be associated with a user. Without being link to a user, it would not have any meaning in our app. In this case, instead of using a weak reference, we would use an unowned reference like this.

class User: NSObject {
    var avatar: Avatar?
}
class Avatar: NSObject {
    unowned var user: User
}

The last scenario is one in which neither member of the relationship should ever be nil.

For example, between two objects, Car and Licence plate number. Neither should exist without a link to the other.

class Car {
    var licencePlateNumber: LicencePlateNumber!
    init(licencePlateNumber: LicencePlateNumber) {
        self.licencePlateNumber = LicencePlateNumber(car: self)
    }
}
class LicencePlateNumber {
    unowned let car: Car
    init(model: String, licencePlateNumber: LicencePlateNumber) {
        self.car = car
    }
}

Here you see that we combined one explicitly unwrapped optional in the Car class, along with an unowned reference pointing from the LicencePlateNumber to the Car.

Like how the blocks in Objective-C are susceptible to memory issues, it isn’t surprising that their Swift counterpart, Closures, are as well. Now, a closure could cause a strong reference cycle in a few ways. But generally it would occur when you assign a closure to a property of a class instance and that closure itself is actually capturing the instance. For more information about closures and capture lists.

Discover and read more posts from Fazeel Akhter
get started