Basic principles of iOS MRC memory management

I’ve been working on iOS for almost a year and haven’t written much about it, so I’m going to get down to business!

Start with memory management. For a software application, the proper use of memory can make the performance and experience of the application better. Memory management on Android was relatively simple, and with the JVM’s powerful garbage collection capabilities, developers only had to worry about common memory leaks.

Once you move to iOS, you’ll need to learn a little bit about memory management and improve your code posture.

After reading through the official Advanced Memory Management Programming Guide, write a summary of my understanding.

Reference counting and MRC

The core of iOS memory management is to manage Reference Counting. The full name of MRC is Mannul Reference Counting. So, iOS memory management is about managing reference counts of objects manually.


Each object is held by several objects, and others, such as holding and abandoning an object, actually make the object’s reference count +1 or -1. When an object’s reference count is reduced to zero, it is immediately released.

Sending a retain message to an object causes the object’s reference count to +1, and sending a release message to an object causes the object’s reference count to -1. The object is immediately released when its reference count reaches zero.

The rules are fairly simple!

But come out mix sooner or later must return, the rule is simple, means when using need all sorts of convention to ensure not to give a problem.

For example, one of the most obvious problems is the attribution of reference counts. Since I can use release for reference count -1, can I release anything I want? Absolutely not. So maybe you’re using an object that gets called Release by some other object and gets released, which is definitely not going to work.

Reference count attribution problem under simple rule based on reference count

To ensure that objects are retained and released correctly, there are a number of rules that constrain developers to manage reference counting correctly.

Calling an object created by a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”, and the method caller is responsible for subsequent calls to release will reference count -1

The rules, real Apple, are simple.

{
    Person *aPerson = [[Person alloc] init];
    // ...
    NSString *name = aPerson.fullName;
    // ...
    [aPerson release];
}
Copy the code

As shown in the example above, the aPerson object is obtained through the alloc method, aPerson has a reference count of 1 and is responsible for subsequent releases.

The name is obtained from the function fullName and does not start with the keyword mentioned above, so it is not responsible for subsequent releases.

Retain +1 for the reference count of the object and is responsible for sending subsequent release messages
{
    Person *aPerson = [Person person];
    [aPerson retain];
    // ...
    [aPerson release];
}
Copy the code

General principles of attribution

To sum up, “Whoever increases the reference count by n is responsible for decreasing it.”

Some cases that require special treatment

Use autoRelease to send delayed release operations

The release operation mentioned above will count the reference to the object immediately to -1, which in many cases will cause the object to be released immediately.

- (NSString *)fullName {
    NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
                                          self.firstName, self.lastName] autorelease];
    return string;
}
Copy the code

In the above example, the caller gets a string object by calling fullName, but according to the principle mentioned above, the caller is not responsible for subsequent calls to release, which are made within the fullName function. But after release the string object is freed immediately, and the caller can’t get it.

To solve this problem, use autoRelease, which, as the name suggests, auto-release, and actually work with auto-release pools.

Objects that send autoRelease messages in the autorelease pool will be sent a release message at the end of the autorelease pool to delay the release.

@autoreleasepool {
    // Code that creates autoreleased objects.
    NSString *string = [[[NSString alloc] initWithFormat:@"%@ %@",
                                          self.firstName, self.lastName] autorelease];
}
Copy the code

For auto-release pools, the AppKit and UIKit frameworks automatically wrap an auto-release pool for each Runloop. Therefore, it is usually not necessary to create an automatic release pool yourself. However, there are three situations in which you might use your own auto-release pool:

  • If you are writing a program that is not based on a UI framework, such as a command-line tool.
  • If you write a loop to create many temporary objects. You can use the AutoRelease pool block in the loop to process these objects before the next iteration. Using auto-free pool blocks in loops helps reduce the maximum memory footprint of your application.
  • If a child thread is generated, you must create your own auto-release pool block once the thread starts executing; Otherwise, your application will leak objects.

You don’t hold objects returned by reference

In some methods of Cocoa, it is specified that an object (ClassName ** or ID *) is returned by reference. A common pattern is to use an NSError object, which contains information about the error when it occurs.

When you execute these methods, you don’t own the object returned by reference, so you don’t need to call Release to release it. Here’s an example:

NSString *fileName = <#Get a file name#>;
NSError *error;
NSString *string = [[NSString alloc] initWithContentsOfFile:fileName
                        encoding:NSUTF8StringEncoding error:&error];
if (string == nil) {
    // Deal with error...
}
// ...
[string release];
Copy the code

To retrieve an object using a secondary pointer, there’s a lot of use for this in Cocoa, the classic one is initWithContentsOfFile in the example above, it’s very easy to retrieve an object using a secondary pointer. However, this method of obtaining objects does not comply with the principle of reference counting mentioned above, so there is an added rule here.

conclusion

The MRC rules are fairly simple:

Sending a retain message to an object causes the object’s reference count to +1, and sending a release message to an object causes the object’s reference count to -1. The object is immediately released when its reference count reaches zero.

On top of simple rules, many rules need to be agreed upon to satisfy and perfect complex development scenarios. Fortunately, iOS development has long since moved to ARC (automatic reference counting). With ARC, we can write Java code without having to understand and manage reference counts because the system already does it for us.

However, ARC only helps developers to insert proper reference count management code in the compiler. Knowing the details of reference counting helps us to understand memory management at a deeper level and improve our technical depth and ability.

I have translated several official documents. If necessary, you can have a look. The quality of official documents is quite high. Have the demand can click to read the original view.

This article is formatted using MDNICE