preface

There are a lot of study notes and blogs about iOS memory management. Thank you very much for learning from them. This article is just based on my own summary. Practice:

  • IOS – Memory Management (ii)
  • IOS – Memory Management (III)

Memory basics

  • 1. Basic partition of memory:

    1. Heap: Managed by the system. Addresses are assigned from high to low. First in, last out. Will save some local variables, function jump jump when on-site protection (register value saved in recovery), these systems will help us automatically achieve, without our intervention. So a large number of local variables, deep recursion, and function loop calls can exhaust stack memory and cause a program to crash.

    2. Stack area: We need to manage the memory ourselves, alloc applies for memory release to release the memory. This is where all the objects are created. Addresses are assigned from low to high. The heap is the memory shared by all programs, and when N such memory is not released, the heap will burst and the program will crash. This is a memory leak.

    3. Global/static area (STAIC) : Global variables and static variables are stored together. Initialized global variables and static variables are stored in one area, and uninitialized global variables and uninitialized static variables are stored in another area adjacent to each other. The system is released after the program ends.

    4. Constant area: This is where constant strings are placed, as well as const constants.

    5. Code area: Store THE App code, and the App program will be copied here.

    In iOS, data exists in the heap and on the stack, whereas our memory management manages the memory on the heap, not the memory on the stack. (Note: in the case of ARC, OC objects on the heap are actually managed by the system.)

    • The underlying data classes are stored on the stack
    • OC objects are stored on the heap
  • 2. Reference count

    Reference counting is a memory management technique in computer programming languages. It is the process of storing the number of references to a resource (object, memory, disk space, etc.) and releasing it when the number of references reaches zero. Automatic resource management can be achieved using reference counting techniques. Reference counting can also refer to garbage collection algorithms that use reference counting techniques to reclaim unused resources.

    When creating an object instance and apply on the heap memory, the object reference count is 1, need to hold the object in the other object, you need to add the object reference counting 1, need to release an object, it will be the object of reference counting minus 1, until the object reference count is zero, the object’s memory will be released immediately.

    In the distant past, memory management for iOS development was to handle reference counts manually, making reference counts -1 where appropriate until they were reduced to 0 and memory was freed. Current iOS development memory management uses ARC, which automatically manages reference counts and monitors the lifetime of objects based on reference counts by automatically inserting appropriate memory management code into existing code at compile time and making some optimizations at Runtime.

Memory management basics

  • 1. Automatic Reference Counting (ARC)

    Automatic Reference Counting is one of the biggest and most exciting changes cited in ARC, WWDC2011 and iOS5. Using ARC, a feature of the new LLVM3.0 compiler, eliminates the manual memory management problem that iOS developers hate. The compiler manages the memory of the object, inserts retain, release, and autorelease in the appropriate places, and generates the correct code to automatically release or retain the object.

    Ownership modifiers: To work with objects in Objective-C programming, variable types can be defined as id types or various object types. The id type and object class in ARC must have an ownership modifier attached to their type.

    There are four ownership modifiers:

    • __strong
    • __weak
    • __unsafe_unretaied
    • __autoreleasing

    The property and ownership modifiers correspond as follows:

    • The ownership type of assign is __unsafe_unretained
    • The corresponding ownership type of copy is __strong
    • The corresponding ownership type of retain is __strong
    • The corresponding ownership type of strong is __strong
    • Unsafe_unretained ownership is __unsafe_unretained
    • The corresponding ownership type of weak is __weak

    __strong

    __strong indicates a strong reference, corresponding to the strong used in defining property. When an object does not have any strong references to it, it is released. If a reference is declared without a modifier, it defaults to a strong reference. When you want to release strong references to objects, you need to ensure that all strong references to objects are set to nil. The __strong modifier is the default ownership modifier for ID types and object types.

    Principle:

    1. Objects are generated by alloc methods

    {
     id __strong obj = [[NSObject alloc] init];
    }
    Copy the code
    Obj = objc_msgSend(NSObject,@selector(alloc)); objc_msgSend(obj,@selector(init)); // call objc_release(obj) when out of scope;Copy the code

    Although you can’t use the release method when ARC is in effect, you know that the compiler inserts release automatically.

    2. Objects are generated by methods other than alloc, new, copy, and multyCopy

    {
     id __strong obj = [NSMutableArray array];
    }
    Copy the code

    The results were slightly different:

    Id obj = objc_msgSend(NSMutableArray,@selector(array)); objc_retainAutoreleasedReturnValue(obj); objc_release(obj);Copy the code

    The operation of objc_retainAutoreleasedReturnValue function is mainly used in the optimization procedure. This is the function that holds a retain object that should return either the method registered with the object in autoreleasePool or the return value of the function. As in the source code, the function is inserted by the compiler after the array class method is called.

    This objc_retainAutoreleasedReturnValue function exists as a pair, and the matching function is objc_autoreleaseReturnValue. It is used in the implementation of objects returned by array class methods. Here’s how the NSMutableArray array method is converted by the compiler:

    {
     id __strong obj = [[NSObject alloc] init];
    }
    Copy the code
    Array {id obj = objc_msgSend(NSMutableArray,@selector(alloc)); objc_msgSend(obj,@selector(init)); // Call autoRelease insteadreturn objc_autoreleaseReturnValue(obj);
    }
    Copy the code

    We can see that the objc_autoreleaseReturnValue function is called and that it returns objects registered with the auto-release pool. However, this function has the advantage of looking at the caller’s command execution list, If it is found that the next will be called objc_retainAutoreleasedReturnValue is not the object returned to registration to autoreleasePool just returns an object. An optimal effect is achieved. The diagram below:

    __weak

    __weak indicates a weak reference, corresponding to the weak used in defining property. Weak references do not affect the object’s release, and when an object is released, all weak references to it are set to nil, preventing wild Pointers. Variables decorated with __weak are used with objects registered in autoreleasePool. One of the most common uses of __weak is to avoid loops. Note that the __weak modifier can only be used with iOS5 and older. Instead, use the __unsafe_unretained modifier for iOS4 and later.

    Several usage scenarios for __weak:

    • Prevent circular references in the Delegate relationship.
    • Prevent circular references in blocks.
    • Decorates controls that point to controls created by the Interface Builder. For example: @property (weak, nonatomic) IBOutlet UIButton *testButton; .

    Principle:

    1. Objects are generated by alloc methods

    {
     id __weak obj = [[NSObject alloc] init];
    }
    Copy the code
    // the compiler transforms the simulated code id obj; id tmp = objc_msgSend(NSObject,@selector(alloc)); objc_msgSend(tmp,@selector(init)); objc_initweak(&obj,tmp); objc_release(tmp); objc_destroyWeak(&object);Copy the code

    __weak memory management also uses a hash table similar to a reference counter table, which registers the address of the memory address of the object as the key and the address of the corresponding __weak modifier variable as the value in the weak table. In the above code, objc_initweak does this. Objc_destroyWeak destroys the corresponding value of the object. When the object is destroyed, the __weak modifier variable is searched in the weak table according to its memory address and deleted from the weak table. So, the weak modifier only adds records to the weak table and does not change the reference count table.

    Object releases object memory with objc_release as follows:

    • objc_release
    • Because the reference count is 0, dealloc is executed
    • _objc_rootDealloc
    • objc_dispose
    • objc_destructInstance
    • objc_clear_deallocating

    Objc_clear_deallocating was last called when the object was abandoned, which does the following:

    Object releases object memory with objc_release as follows:

    • Obtain all records corresponding to memory addresses of obsolete objects from the weak table
    • Set all variables decorated with weak in the record corresponding to the memory address of the obsolete object to nil
    • Delete the memory address of the obsolete object from the weak table
    • Delete a discarded object from the reference count table based on its memory address
    • This can explain why all the corresponding pointer variables of weak were set to nil when the object was destroyed. At the same time, it can also be seen that there are many steps to destroy weak. If weak is used in large quantities, the load of CPU will be increased

    You also need to make sure that variables that use the __weak modifier are using objects registered with autoreleasePool.

    {
       id __weak obj1 = obj; 
       NSLog(@"obj2-%@",obj1);
    }
    Copy the code
    // The compiler converts the code as follows: id obj1; objc_initweak(&obj1,obj); id tmp = objc_loadWeakRetained(&obj1); objc_autorelease(tmp); NSLog(@"% @",tmp);
    objc_destroyWeak(&obj1);
    Copy the code

    Retained. The objc_loadWeakRetained retains the object referenced with the __weak modifier variable and retains it. The objc_Autorelease retains the object in autoreleasePool. You are actually accessing objects registered with the auto-release pool. Therefore, if weak is used in large numbers, a large number of objects will be registered to the automatic release pool when we attempt to access the weak modified object, which will affect the performance of the program and may also have been released.

    Why does an object accessing the weak modifier access an object registered to the auto-release pool?

    Because weak does not cause the reference counter of the object to change, it is likely that the object will be released during runtime. Therefore, you need to register the object in the automatic release pool and free the memory used by the object when the autoreleasePool is destroyed.

    Solution: To access the weak variable, assign it to a strong variable and then access it.

    __unsafe_unretained

    ARC was introduced in iOS5, and the __unsafe_unretained modifier was primarily intended to be compatible with iOS4 and older systems when ARC was first released, because they didn’t have weak references. This modifier is unsafe_unretained when defining a property. The __unsafe_unretained modifier pointer points purely to the object without any additional operations. Holding the object makes the retainCount +1. And when the object is released it still points to the original address of the object, it’s not automatically set to nil, so it’s a wild pointer, very unsafe.

    __unsafe_unretained: Across ARC but with ios4. x, __unsafe_unretained instead of __weak solves the problem of strong repetition.

    __autoreleasing

    Assigning an object to a variable with the __autoreleasing modifier is the same as calling the object’s autorelease method in MRC.

    Id __autoreleasing obj = [[NSObject alloc] init]; }Copy the code
    Id pool = objc_autoreleasePoolPush(); id obj = objc_msgSend(NSObject,@selector(alloc)); objc_msgSend(obj,@selector(init)); objc_autorelease(obj); objc_autoreleasePoolPop(pool); @autoreleasepool { id __autoreleasing obj = [NSMutableArray array]; }Copy the code
    Id pool = objc_autoreleasePoolPush(); id obj = objc_msgSend(NSMutableArray,@selector(array)); objc_retainAutoreleasedReturnValue(obj); objc_autorelease(obj); objc_autoreleasePoolPop(pool);Copy the code

    The above two ways, although the second holding the object’s methods from the alloc method into objc_retainAutoreleasedReturnValue function, but the same is through objc_autorelease, registered in the autoreleasePool.

  • 2. MRC Manual Reference Counting – A brief introduction

    The added reference count in the MRC has to be released manually, so we need to know which way to cause the reference count to +1

    The object operation Corresponding method in OC Reference count changes
    Generate and hold objects alloc/new/copy/mutableCopy + 1
    Hold the object retain + 1
    Release object release – 1
    Discarded objects dealloc Return 0

    Four principles

    • Self generated objects, own
    • Objects that are not generated by themselves can be held by themselves.
    • Release objects when you no longer need to hold them yourself.
    • Objects that are not owned by the owner do not need to be released.
  • 3. Circular references

    What is a circular reference? A circular reference is when two objects are strongly referenced to each other, and the reference count is increased by one. As we said earlier, objects are released only when the reference count is reduced to zero. But the reference counts of the two depend on each other, so they can never be released

    The two situations where circular references are most likely to occur are Delegate and Block. So we introduced the concept of weak references, which hold objects but do not increase the reference count, thus avoiding circular references. This is what the ownership modifier __weak does above. About principle in __weak part are described, simple description is anyone with a weak reference object has a pointer address table to save a weak reference, but the weak references will not make the object reference count + 1, so when the object reference count becomes zero, the system through this table, find all the weak reference pointer into a nil

    So memory management in ARC is all about finding these memory Leaks, and Instrument provides tools like Allocations/Leaks to detect memory Leaks.

    Similar to the principle of the memory leak tool MLeaksFinder, the principle is as follows:

    We know that when a UIViewController is pop or dismiss, the UIViewController including its view, its subviews and so on will be released very quickly (unless you design it as a singleton, or hold a strong reference to it, But this is rarely done). So, we just need to see if the UIViewController, its view, its subviews and so on still exist after a ViewController is popped or dismissed for a little while.

    To do this, add a method to the base class NSObject, the -WillDealloc method, which points to self with a weak pointer and, after a short period of time (3 seconds), calls -AssertNotDealloc with the weak pointer. -AssertNotDealloc is used for direct middle assertion.

    - (BOOL)willDealloc {
       __weak id weakSelf = self;
       dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
           [weakSelf assertNotDealloc];
       });
       return YES;
    }
    - (void)assertNotDealloc {
       NSAssert(NO, @“”);
    }
    Copy the code

    So, when we think an object should be released, call this method before release. If 3 seconds later it is released successfully, weakSelf points to nil, does not call the -AssertNotDealloc method, and does not assert. If it is not released (leaked), -AssertNotDealloc will be called in the assertion. So, when a UIViewController is pop or dismiss (we think it should be released), we go through all the views on that UIViewController, we go to -WillDealloc, and if it’s not released after 3 seconds, It goes into the assertion.

Postscript:

Specific code practice please move

IOS – Memory Management (ii)

IOS – Memory Management (III)

Reference documentation

  • Brief introduction to iOS memory management mechanism
  • Memory management in iOS
  • IOS memory management details