An overview of the

In iOS development, we’ve all heard of memory management at some point. IOS memory management generally refers to the MEMORY management of OC objects, because OC objects are allocated in the heap memory, heap memory needs to be allocated and reclaimed dynamically by programmers; The underlying data types (non-OC objects) are allocated in stack memory, and when out of scope are detected and reclaimed by the system. If we don’t manage memory properly during development, we can cause memory leaks.

Memory management, as we usually refer to it, is actually divided into two phases from a developmental perspective: MRC and ARC. MRC refers to manual memory management. During the development process, developers need to write the code of memory management manually. ARC stands for automatic memory management in which the code for memory management is generated by the LLVM compiler and the OC runtime library.

This article mainly introduces the principle of memory management and the difference between ARC and MRC environment to write code.

The outline

One reference count
Method of MRC operation object
  1. alloc/new/copy/mutableCopy
  2. retain
  3. release
  4. autorelease
  5. autorelease pool
Three ARC modifiers to operate on objects
  1. __strong 1.1 Strong and variable 1.2 strong and attribute 1.3 strong implementation
  2. __weak 2.1 weak and loop references 2.2 weak and variable 2.3 weak implement 2.3.1 weak and assign 2.3.2 weak and access
  3. __unsafe_unretained
  4. _autoreleasing

Detailed introduction

1. Reference counting

In OC, reference counting is used for memory management. Each object has a reference counter corresponding to it. When an object is held, the reference count of the object is incremented. When a holding of the object is released, the object’s reference count is decremented. When the object’s reference count becomes zero, the object is reclaimed by the system.

When an object runs out of use and is not released, its reference count is always greater than 1. The object will always occupy the space allocated to the heap, resulting in a memory leak. Memory leaks to a certain extent may lead to memory overflow, which may cause the program to crash.

Second, MRC operation object method

1.alloc/new/copy/mutableCopy

1.1 Hold the caller’s own object

In apple’s rules, using alloc/new/copy/mutableCopy create all the object returned to the caller, for example the following MRC code:

NSMutableArray *array = [[NSMutableArray alloc] init]; /*NSMutableArray class object A*/ NSLog(@"%p", array); [array release]; / / releaseCopy the code

Since object A is generated by alloc, in compliance with Apple regulations, the pointer variable array points to and holds object A, and the reference counter is incrementing by one. In addition, array needs to release object A after it is finished using it. When release is called, its reference to object A is released, and the counter decreases by 1. Object A now has zero reference count, so object A is reclaimed. Unable to access objects that have been reclaimed, a crash occurs.

1.2 Holding objects not owned by the caller

When holding objects that are not owned by the caller, for example:

    id obj = [Person person];
    
    [obj retain];
    
    /*do something*/
    
    [obj release];
Copy the code

At this point, the obj variable acquires but does not hold the Person class object, which can be retained. When we are done with the object, we should call the release method to release it.

Note: according to the naming rules of apple, must be the alloc/new/copy/mutableCopy beginning, and is in line with the hump naming rules generated objects owned by the caller. For example, the following method generates an object that is not owned by the caller:

- (id)newarray;
- (id)allocwithInfo;
- (id)coPySomething;
- (id)mutablecopyItem;
Copy the code

2.retain

2.1 Retain and properties

We can store objects by properties. If a property is strongly referenced, we can manipulate an object by instance variables and accessor methods of the property. For example, setter methods for a property are as follows:

- (void)setPerson:(Person *)person {

    [person retain];
    
    [_person release];
    
    _person = person;
    
}
Copy the code

We retain the new value, release the old value, and update the value to the instance variable. One thing to note is that you need to retain the new value before releasing it. If the new value is the same object as the old value, release may cause the object to be reclaimed by the system and then retain will not make any sense. Take this example:

#import "ViewController.h" #import "Person.h" @interface ViewController () @property (nonatomic, strong)Person *person; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // The instance variable holds the Person class object (P object). This assignment does not call the set method _person = [[Person alloc] init]; self.person = _person; } - (void)setPerson:(Person *) Person {//release releases the reference to the P object, and the P object is reclaimed by the system. Person retain [person retain]; person retain [person retain]; _person = person; } @endCopy the code

Since the P object is reclaimed, its allocated memory is placed in the “available memory pool.” If the memory is not overwritten, the P object is still valid; If memory is overridden, the instance variable _person points to a pointer to an overridden unknown object, and the instance variable becomes a “dangling pointer.”

2.2 Retain and Arrays

If we add an object to an array, the array’s addObject method calls the retain method on the object. For example:

Person *person = [[person alloc] init]; NSMutableArray *array = [NSMutableArray array]; // Person is added to the array, and the P reference count is 2 [array addObject:person];Copy the code

At this point, object P is held by both the person and array variables.

3.release

3.1 Release the objects held by yourself

When we hold an object, we need to release it if we don’t need to use it anymore. For example:

NSArray *array = [NSArray alloc] init]; /* When the object is no longer needed, release */ [array release]; //obj gets but does not hold the object id obj = [NSArray array]; // retain object [obj retain]; */ [obj release] */ [obj release];Copy the code
3.2 Do not release objects that are not owned by you

When we release an object without holding it, the application crashes.

Person *p = [[Person alloc] init]; //Person class object A //p releases A strong reference to object A, the owner of object A does not exist // Object A reference count is zero, so object A is reclaimed [p release]; [p release]; // Free objects that are not owned by youCopy the code

Also, we cannot access an object that has already been freed, and that object’s heap space would crash if overwritten.

4.autorelease

Autorelease refers to autorelease. When an object receives an AutoRelease, it is registered in the autoRelease pool that is currently at the top of the stack. If no auto release pool is actively generated, the current auto release pool corresponds to the auto release pool of the main running loop. Before the current thread’s RunLoop goes to sleep, a release operation is performed on all objects registered to the auto-release pool.

The difference between an autorelease and a release is that a release immediately releases a strong reference to an object; Autorelease is the delayed release of an object’s life cycle.

Autorelease is usually used when calling a method that returns an object, as in the following code:

Person *p = [Person Person]; NSLog(@"%p", p); // use no retain // hold requires retain [p retain]; _person = p; [_person release]; Person * Person = [[Person alloc] init]; Person * Person = [[Person alloc] init]; //[person release]; [person autorelease]; return person; }Copy the code

Called externally, as you know from the method name person, the object created is obtained but not held by the p pointer variable. Inside the function, Person acquires and holds the Person class object, and the reference count of the returned Person object increases by one. In other words, the caller needs to handle the extra hold operation. In addition, we cannot call release inside a function, otherwise the object will be recycled before it is returned. Autorelease is a good way to solve this problem.

Simply calling the autoRelease method on the object to be returned and registering with the auto-release pool extends the life of the Person object so that it can survive the autoRelease Pool destruction (drain).

In addition, the Person object calls the AutoRelease method when it returns. The object is already in the automatic release pool and we can use object P directly without having to access it through [P retain]; However, if you want to hold the object with an instance variable, you will need to do a retain operation on variable P and release the object when the example variable is finished using it.

5. autorelease pool

5.1 AutoRelease Pool and RunLoop

Each thread contains a corresponding auto-release pool, which is destroyed when a thread is terminated. At the same time, objects in the auto-release pool will perform a release operation.

When an application is started, the system starts a thread by default. This thread is called the main thread. The main thread also has a corresponding auto-release pool, such as the common ARC main.h file:

int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); }}Copy the code

The auto-release pool is used to release objects registered to the auto-release pool under the main thread. Note that when we start a sliver thread and start RunLoop, we need to add an Autorelease pool to it to help keep memory safe.

5.2 AutoRelease pool and reduce peak memory

When we perform complex operations, especially if the complex operations are to be executed in a loop, temporary variables are inevitable. When more and more objects are added to the main thread auto-free pool, they are not freed in time, resulting in memory overflow. At this point, we can manually add automatic release pools to solve the problem. As shown in the following example:

for (int i = 0; i < largeNumber; I++) {NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; / / produce many are registered to automatically release the temporary pool object id obj = [Person personWithComplexOperation]; [pool drain]; }Copy the code

As the example above shows, the number of loops we execute is a very large number. And call personWithComplexOperation method will produce many temporary objects, in the process of temporary objects could be generated by the registered with the automatic release in the pool. We can effectively reduce peak memory by manually generating an autofree pool and releasing the objects in the pool before the end of each loop.

Three ARC modifiers to operate on objects

1. __strong

1.1 __strong and variables

In ARC mode, the default ownership modifier for ID types and OC objects is __strong. When a variable is decorated with the __strong modifier, it is discarded when it is out of scope. At the same time, the object assigned to the variable is also released. Such as:

Person *p = [Person Person]; //Person __strong *p = [Person Person]; } // the Person class object holder does not exist, and the object is releasedCopy the code

The MRC code for the above example is as follows:

{
    Person *p = [Person person];
    
    [p retain];
    
    [p release];
}
Copy the code

As can be seen, ARC and MRC are corresponding. In ARC, however, the memory management code for objects “hold” and “release” is left up to the system to call.

1.2 Strong and Attributes

If the modifier of a property is strong, the compiler adds a method to release the object held by the instance variable in the dealloc method of the class to which the instance variable belongs. Under MRC, our dealloc method is like:

- (void)dealloc { [p release]; //ARC invalid [super dealloc]; / / ARC invalid}Copy the code

In ARC, we don’t have to write such code anymore. Also, in the dealloc method, ARC can only help us with OC objects. If the instance variable holds a non-OC object like CoreFoundation, we need to reclaim it manually:

- (void)dealloc {

    CFRelease(_cfObject);
}
Copy the code

Under ARC, the dealloc method is typically used to perform two tasks. The first is to manually release non-OC objects; The second is contact monitoring. Also, we cannot actively call the dealloc method under ARC. Because once dealloc is called, the object is no longer valid. The system will automatically call the method when the time is right.

1.3 Implementation of strong

In ARC, optimizations have been made in addition to the “save” and “release” methods being called automatically. For example, if an object has been reserved and freed more than once, ARC may remove the reserved and freed objects in pairs for special cases. Such as:

+ (id)person { Person *tmp = [[Person alloc] init]; // Reference count is 1 [TMP autorelease]; // Register with automatic release pool (ARC invalid) return TMP; } { //ARC _p = [Person person]; Person *p = [Person Person]; //Person class object reference count is 1 _p = [p retain]; // increment to 2 [_p release]; // Empty the auto release pool, and the Person object is decrement to 0 to release itCopy the code

In the above code, ARC corresponds to a concrete implementation of MRC shown. In the above demonstration, the +(ID)person method calls an autorelease internally to delay the life cycle of the object it returns, and releases later in the autorelease pool. _p holds the object via retain and releases when it’s done. Retain and autorelease are redundant and can be simplified as follows:

+ (id)person { Person *tmp = [[Person alloc] init]; // Reference count is 1 return TMP; } { //ARC _p = [Person person]; _p = [Person Person]; // The Person object reference count is 1 [_p release]; // Decrement to 0, Person object is reclaimed}Copy the code

How does ARC decide whether to remove this pair of operations? Retain and autorelease operations are not performed directly in ARC. Instead, the retain and autorelease operations are performed using two methods:

objc_autoreleaseReturnValue(obj); / / corresponding autorelease objc_retainAutoreleasedReturnValue (obj); / / corresponding to retainCopy the code

The pseudocode corresponding to the two methods is shown below:

Id objc_autoreleaseReturnValue(id obj) {if (" return retain from obj ") {// yes set_flag(obj); // set the flag bit return obj; } else { return [obj autorelease]; {if}} id objc_retainAutoreleasedReturnValue (obj) (get_flag (obj)) {/ / a flag bit return obj. } else { return [obj retain]; }}Copy the code

Through the above two pieces of pseudocode, let’s comb the code again:

+ (id)person { Person *tmp = [[Person alloc] init]; Return objc_autoreleaseReturnValue(id TMP); } { //ARC _p = [Person person]; Person *p = [Person Person]; / / the Person class object reference count is 1 _p = objc_retainAutoreleasedReturnValue (p); // increment to 1 [_p release]; // decrement to 0}Copy the code

As you can see from the sample code above:

  1. The objc_autoreleaseReturnValue method is executed inside the Person method before we get the object returned by the person method using the variable P. This method detects the piece of code that will be executed after the object is returned, and if that piece of code executes the retain method on the returned object, it sets a flag bit in the global data structure for the object and returns the object directly. Instead, register the object with the automatic release pool before returning.

  2. When we perform retain operations with the Person class objects, executes objc_retainAutoreleasedReturnValue method. This method checks whether the corresponding object has been set flag bit, if so, directly returns the object; Otherwise, a retain operation is performed on the object and returned.

By setting and detecting flag bits in ARC, redundant pairs (” hold “&” release “) can be removed to optimize program performance.

2. __weak

2.1 Weak and circular references

__weak corresponds to __strong, which has a strong reference to an object, as we mentioned above. __weak, then, means having a weak reference to an object. Weak is used to solve the problem of circular reference in our development, such as the following code:

/*Man class */ #import <Foundation/ foundation.h > @class Woman; @interface Man : NSObject @property (nonatomic, strong)Woman *person; @end /*Woman class */ #import <Foundation/ foundation.h > @class Man; @interface Woman : NSObject @property (nonatomic, strong)Man *person; @end /* call */ - (void)viewDidLoad {[super viewDidLoad]; Man *man = [[Man alloc] init]; Woman *woman = [[Woman alloc] init]; man.person = woman; woman.person = man; }Copy the code

As you can see from the above code, the Man and Woman objects have a Person property with the ownership modifier strong. The two classes are strongly referenced to each other through instance variables. If a Man object wants to be released, the Woman object needs to send a release message to it. However, for the Woman object to execute the Dealloc method to send a release message to the Man object, the Man object also needs to send a release message to it. Both instance variables strongly refer to each other, resulting in circular references, as shown in the figure below:





PNG for circular reference

This is where the weak modifier comes in handy, because weak only refers to an object by weak reference and does not actually hold the object. Therefore, we only need to modify one of the attributes of the above two class objects with weak to solve the problem of circular reference. For example, if we modify the person attribute of class Woman with weak, the analysis code is as follows:

/* call */ - (void)viewDidLoad {[super viewDidLoad]; Man *man = [[Man alloc] init]; //Man object reference count is 1 Woman * Woman = [[Woman alloc] init]; //Woman object reference count is 1 man.person = Woman; // strong reference, Woman object reference count is 2 woman.person = man; //Man is out of scope. Strong references to Man are invalid. //Man is invalid. //Woman is out of scope. Strong references to Woman objects are invalid. //Woman object holders do not exist, Woman objects are reclaimedCopy the code

As you can see from the sample code above, we only need to change one of the strong references to a weak reference to break the circular reference. Similar circular references are common block, NSTimer, delegate, etc. If you are interested in them, please refer to relevant information by yourself. I will not introduce one by one here.

Also, based on the runtime library, if a variable or property is decorated with weak, it is automatically assigned nil when the object it points to is reclaimed. This is an effective way to avoid crashes caused by wild Pointers, but it is strongly discouraged to use a weak reference to an object that has already been reclaimed, which is itself a programming bug.

2.2 Weak and variables

If a variable is __weak, it has a weak reference to the object to which it points. For example:

Person __weak *weakPerson = nil; if (1) { Person *person = [[Person alloc] init]; weakPerson = person; NSLog(@"%@", weakPerson); //weakPerson weak reference} NSLog(@"%@", weakPerson);Copy the code

The following output is displayed:

[1198:73648] <Person: 0x600000018120>
[1198:73648] (null)
Copy the code

As you can see from the above output, strong references to the Person object by the Person variable fail when out of scope. The Person object holder does not exist, so the object is reclaimed. At the same time, the weak reference of the weakPerson variable to Person fails, and the weakPerson variable is assigned to nil.

Also note that if a variable is decorated with weak, the variable cannot hold the object sample and the compiler will issue a warning. For example:

Person __weak *weakPerson = [[Person alloc] init];
Copy the code

Because weakPerson is __weak, it cannot hold the generated Person class object. So the Person class object is released immediately after creation, so the compiler gives the corresponding warning:

Assigning retained object to weak variable; object will be released after assignment

2.3 Weak implementation
2.3.1 Weak and assigned values

To explain the implementation of the weak assignment, let’s look at the following example code:

    Person *person = [[Person alloc] init];
    
    Person __weak *p = person;
Copy the code

The corresponding simulation code above is as follows:

    Person *person = [[Person alloc] init];
    
    Person *p;
    objc_initWeak(&p, person);
    objc_destroyWeak(&p, 0);
Copy the code

The above two functions are used to initialize and release p respectively, and they both call the same function. As follows:

id p; p = 0; objc_storeWeak(&p, person); // objc_initWeak objc_storeWeak(&p, 0); / / the corresponding objc_destroyWeakCopy the code

When we point to an object with a weak reference to a variable, the objc_storeWeak method is called by passing in the address of the variable and the assignment object. This method internally registers the address &person of the object as the key and the address &p of the variable P into the weak table.

When out of scope and the Person object is reclaimed, call objc_storeWeak(&p, 0) to remove the address of the variable from the weak table. Before the variable address is deleted from the weak table, the address of the reclaimed object is retrieved as the key value, and the corresponding variable address is assigned to nil.

2.3.2 Weak and Access

When we access an object pointed to by an __weak modified variable, how is it implemented internally? Let’s start with the following example code:

    Person *person = [[Person alloc] init];

    Person __weak *p = person;
    
    NSLog(@"%@", p);
Copy the code

The simulation code corresponding to the above code is as follows:

    Person *person = [[Person alloc] init];
    
    Person *p;
    objc_initWeak(&p, person);
    id tmp = objc_loadWeakRetained(&p);
    objc_autorelease(tmp);
    NSLog(@"%@", tmp);
    objc_destroyWeak(&p);
Copy the code

As you can see from the above code, accessing a variable with an __weak modifier takes two more steps than assignment:

  1. Objc_loadWeakRetained, retained the object referenced by the corresponding variable through this function.
  2. Registers the object to which the variable points to in the automatic release pool.

In this way, the object referenced by the weak variable is added to the automatic release pool. The object is safe to use until the auto-release pool ends. Note that if the weak variable is accessed in large numbers, the object referenced by the weak variable will be added to the automatic release pool multiple times, which can affect performance. If we need a lot of access, we do this with a variable modified by __strong, for example:

Person __weak *p = obj; Person *tmp = p; // the object referenced by p is registered with the automatic release pool NSLog(@"%@", TMP); NSLog(@"%@", tmp); NSLog(@"%@", tmp); NSLog(@"%@", tmp); NSLog(@"%@", tmp);Copy the code

By utilizing the use of the __strong intermediate variable, objects referenced by P are registered only once into the auto-release pool, effectively reducing the number of objects in the auto-release pool.

3. __unsafe_unretained

The first thing we need to be clear about is that if a variable is __unsafe_unretained, it is not a memory-managed object for the editor. This modifier indicates that the value is not retained, that is, it is neither strongly nor weakly referenced to the object to which it points. For example:





__unsafe_unretained.png

A strong reference to the Person object of the variable P is invalid when out of scope. The holder of the Person class object does not exist and the object is reclaimed. When we use this variable again, a wild pointer crash occurs because the object it represents has been reclaimed. This differs from __weak, which is assigned nil when the object referred to by the __weak modifier is reclaimed.

Also, __unsafe_unretained variables, like __weak, cannot hold object instances. The __unsafe_unretained variable does not retain the generated object instance. Therefore, the object can be reclaimed immediately after creation, and the compiler warns.

When assigning a variable that is __unsafe_unretained, we must ensure that the assignment object exists, or the program will crash.

4. __autoreleasing

In ARC and MRC, autoRelease works the same way, but the way they behave is different. 1. If the returned object belongs to the user, as follows:

    @autoreleasepool {
        
        Person __autoreleasing *p = [[Person alloc] init];
    }
Copy the code

The code simulated above is as follows:

id pool = objc_autoreleasePoolPush(); Person *p = [[Person alloc] init]; / / hold objc_autorelease (p); objc_autoreleasePoolPop(pool);Copy the code

2. If the returned object is not owned by the caller, it looks like this:

    @autoreleasepool {
        
        Person __autoreleasing *p = [Person person];
    }
Copy the code

The code simulated above is as follows:

id pool = objc_autoreleasePoolPush(); Person *p = [Person person]; objc_retainAutoreleasedReturnValue(p); // Hold (optimized retain method) objc_autoRelease (p); objc_autoreleasePoolPop(pool);Copy the code

As you can see from the above two examples, __autoreleasing is implemented through objc_autorelease(), but the holding method is different.

This concludes memory management. Due to the limited technology, there will inevitably be loopholes, welcome to correct. Reprint please indicate the source, thank you very much.

Resources: Objective-C Advanced programming for iOS and OS X Multithreading and memory management Effective Objective 2.0