PrefaceNow iOS development has entered the era of arc and even swift, but memory management is still a key issue. If you only develop blindly without knowing the principles, you will fall into the trap and cannot jump out. Understanding memory management can help us write higher quality code. Memory management is a very important part of program design. Programs consume memory during operation and release the occupied memory after the operation ends. If the program keeps allocating memory while running and does not release the unused memory in time, the following consequences will occur: the memory occupied by the program becomes larger and larger until the memory is exhausted and the program crashes due to lack of memory. We call this a memory leak. ObjC's memory management is relatively simple, but it is not easy to deeply understand it. This article will introduce how to use ObjC for memory management. 1. Reference countingIn ObjC, when will an object be released (or when will the memory occupied by the object be reclaimed)? The answer is: when the object is not referenced by any variable (in other words, there is no pointer pointing to the object), it will be released. How do you know that the object is no longer referenced?ObjC uses reference counting technology to manage: 1) Every object has an associated integer, called a reference counter 2) When the code needs to use the object, the object's reference count is increased by 1 3) When the code finishes using the object, the object's reference count is reduced by 1 4) When the reference count value becomes 0, it means that the object is not used by any code and the object will be released. The corresponding message sending method is as follows: 1) When an object is created (through alloc, new, or copy methods), its reference count is initialized to 1 2) Send a retain message to the object, and its reference count increases by 1 3) Send a release message to the object, and its reference count is reduced by 1 4) When the object reference count returns to 0, ObjC sends a dealloc message to the object to destroy the object Let's use a simple example to illustrate: Scenario: There is a pet center (memory) that can send small animals (objects) to play with children (object referencer). Now Xiaoming wants to play with the puppy. Create a new Dog class and override its creation and destruction methods:
Create a dog object in the main method and send a message to the dog
The output is
As you can see, the reference count helps the pet center to mark the usage status of the puppy and return it to the pet center in time when the task is completed. Consider a few questions:1) NSString reference counting problem If we try to check the reference count of a string
You will find that the reference count is -1, which can be understood as NSString is actually a string constant and has no reference count (or its reference count is a very large value (you can print it using %lu), and performing reference count operations on it has no substantial effect). 2) Assignment does not own an object
This is just a pointer assignment operation, and the reference count of name will not be increased. If you need to hold the object, you must send a retain message. 3) dealloc Since the dealloc method is called when releasing an object, we override the dealloc method to check the object release status. If it is not called, it will cause memory leaks. In the above example, we override dealloc to print a log when the puppy is released to tell us that the release has been completed. 4) In the above example, if we add such an operation
You will find that the reference count obtained is 1, why not 0? This is because when an object with a reference count of 1 is released, the system knows that the object will be recycled and will no longer decrement the object's reference count by 1, which can increase the efficiency of object recycling. In addition, it is not advisable to send messages to released objects, because the memory of the object has been reclaimed. If the memory has been used by other objects when the message is sent, the result is uncertain and may even cause a crash. 2. Automatic release poolNow it is clear that an object should be released when it is no longer used, but in some cases, it is difficult for us to figure out when an object is no longer used (for example, the time when Xiaoming finishes playing with the puppy is uncertain). What should we do? ObjC provides the autorelease method to solve this problem. When an autorelease message is sent to an object, the method will send a release message to the object at some time in the future to release it. During this period of time, the object can still be used. So what is the principle of autorelease? The principle is that when an object receives an autorelease message, it will be added to the current autorelease pool. When the autorelease pool is destroyed, a release message will be sent to all objects in the pool. Here comes the concept of autorelease pool. What is an autorelease pool? As the name suggests, it is a pool that can hold objects and release them automatically, which greatly increases our flexibility in handling objects. How to create an autorelease pool? ObjC provides two methods to create autorelease pools: Method 1: Use NSAutoreleasePool to create
Method 2: Create using @autoreleasepool
After the autorelease pool is created, it becomes an active pool. When the pool is released, the pool will release all the objects it contains. The first method is recommended of the above two methods because it is more efficient to let ObjC manage the memory. When is the autorelease pool created? During the use of the app, the auto-release pool will be automatically generated and destroyed regularly. It is usually created before program events are processed. Of course, we can also create the auto-release pool ourselves to achieve some of our specific purposes. When is the autorelease pool destroyed? The destruction time of the automatic release pool is fixed. It is usually released after program event processing or released manually by ourselves. The following example illustrates the workflow of the automatic release pool: Scenario: Now Xiaoming and Xiaohong both want to play with the puppy, but their needs are different and their playtimes are different. The process is as follows
The output is as follows:
It can be seen that the dog object is released only after the pool is released, so xiaohong can play with the puppy as much as he wants before the pool is released. When using an autorelease pool, please note: 1) The automatic release pool actually just sends a release message to all objects in the pool when releasing. It does not guarantee that the object will be destroyed. If the reference count of the object is still greater than 1 after the automatic release pool sends a release message to the object, the object cannot be destroyed. 2) Objects in the autorelease pool will be released at the same time. If the operation needs to generate a large number of objects that occupy a large amount of memory space, multiple release pools can be used for optimization. For example, if a large number of temporary variables need to be created in a loop, an internal pool can be created to reduce the peak memory usage. 3) Autorelease does not change the reference count of the object Common problems with autorelease pools: When it comes to managing object release, automatically helping us release the pool saves a lot of time, but sometimes it may not achieve the desired effect. For example, in a loop event, if the number of loops is large or the event processing occupies a large amount of memory, it will cause the memory usage to continue to grow, which may lead to undesirable consequences. Sample code:
As mentioned before, the release time of the autorelease pool is fixed. In this example, the autorelease pool will be released at the end of the loop event. Now the question is: in this 100,000-time loop, a string is generated and printed each time. These string objects are placed in the pool and will not be released until the end of the loop, so the memory does not grow during the loop. The solution to this problem is to create a new autorelease pool in the loop, and we decide how many times to release it.
3. iOS memory management rules3.1 Basic principlesThere is no order without rules. In iOS development, there are also rules to constrain developers to manage memory. Generally speaking, there are three points: 1) When you create an object through the new, alloc or copy method, its reference count is 1. When the object is no longer used, you should send a release or autorelease message to the object to release it. 2) When you obtain an object through other methods, if the object reference count is 1 and is set to autorelease, you do not need to perform any operations to release the object; 3) If you intend to take ownership of the object, you need to retain the object and release it after the operation is completed, and you must ensure that the number of retains and releases is equal. Applied to the example at the beginning of the article, every time a child applies for a puppy (generates an object), the puppy must be returned to the pet center (releases the object). If the puppy is only applied for but not returned (the object is created but not released), the puppies in the pet center will become fewer and fewer (the available memory will become less and less), until there are no puppies left (the memory is exhausted), and other children will no longer have any puppies to apply for (no resources to apply for). Therefore, the rules must be followed: applications must be returned (Rule 1), the number of applications must be returned (Rule 3), and if the puppy has a set return time, the child does not need to actively return it (Rule 2). Interested readers may consider: The above principles can be summarized in a concise sentence. What is it? 3.2 ARCIn the MRC era, the above rules must be strictly followed, otherwise memory problems will become a demonic existence. However, in the ARC era, things seem to have become easier. No need to write endless raise and release, which seems to make development easier and more friendly to beginners. ObjC2.0 introduced a garbage collection mechanism. However, since the garbage collection mechanism will have some adverse effects on mobile devices (such as lag caused by garbage cleaning), iOS does not support this mechanism. Apple's solution is ARC (Automatic Reference Counting). After iOS5, we can turn on ARC mode. ARC can be understood as a housekeeper. This housekeeper will help us send retain and release statements to objects. We no longer need to add them manually. We can create or reference objects more comfortably, simplify memory management steps, and save a lot of development time. In fact, ARC is not garbage collection, nor does it mean that memory management is not needed. It is implicit memory management. The compiler will insert appropriate raisen and release statements into the code during compilation, which is equivalent to helping us complete the memory management work behind the scenes. Let's convert the autorelease pool example to ARC.
How about it? Isn’t it a lot simpler? Does it feel familiar? Notice: 1) If your project is old, you can convert it from MRC to ARC to keep up with the times and maintain it better 2) If your project references some libraries that do not support ARC, you can configure the compiler parameters of the corresponding m files to -fno-objc-arc in Compile Sources of Build Phases. 3) ARC can help us simplify memory management problems, but it does not mean that it is perfect. There are still some situations that it cannot handle, which requires us to handle manually, such as circular references, non-OjC objects, malloc() or free() in Core Foundation, etc. Interested readers may consider: What are the disadvantages of MRC? What are the limitations of ARC? Please list them. 3.3 ARC ModifiersARC provides four modifiers: strong, weak, autoreleasing, and unsafe_unretained. __strong: A strong reference that holds ownership of the object it points to. This is the default value when there is no modifier. If you need to force release, you can set it to nil. For example, the timer we often use
Equivalent to
When no longer needed, force destroy the timer
__weak: A weak reference does not hold ownership of the object it points to. After the memory of the object pointed to by the reference is reclaimed, the reference itself will be set to nil to avoid wild pointers. For example, to avoid circular references, use the following weak reference declaration:
__autoreleasing: Automatically releases references to objects, generally used to pass parameters For example, a method to read data
When you call, you will find this prompt
This is the compiler automatically inserting the following code for us
__unsafe_unretained: A product for compatibility with versions below iOS 5. It can be understood as a weak MRC. It is basically not used now and will not be described here. Interested readers may consider: 1) Which one is correct, __strong NSTimer * timer or NSTimer * __strong timer? Why doesn't the compiler report an error? 2) What problems might you encounter when using __autoreleasing? 3.4 Memory Management of AttributesObjC2.0 introduced @property, which provides declarations of member variable access methods, permissions, environments, and memory management types. The following mainly explains the memory management of properties in ARC. The parameters of the attribute are divided into three categories. The default for basic data types is (atomic, readwrite, assign), and the default for object types is (atomic, readwrite, strong). The third parameter is the memory management method modifier of the attribute. The modifier can be one of the following: 1) assign: direct assignment Assign is generally used to modify basic data types @property (nonatomic, assign) NSInteger count; Of course, you can also modify ObjC objects, but it is not recommended, because after the object modified by assign is released, the pointer still points to the memory before the release, which may cause memory problems and crashes in subsequent operations. 2) retain: release the old value, then retain the new value (reference count + 1) Like retain and strong, they are both used to modify ObjC objects. When using the set method to assign a value, it actually retains the new value first, then releases the old value, and then sets the new value, to avoid the problem of the object being released when the new and old values are the same. MRC is written as follows
ARC corresponding writing
3) copy: release the old value and then copy the new value (copy the content) It is generally used to modify objects such as String, Dict, Array, etc. that need to protect their encapsulation, especially when their content is mutable. Therefore, a copy (deep copy) of the content will be used for the attribute to avoid possible changes to the source content. When using the set method to assign a value, it actually copies the new value first, then releases the old value, and then sets the new value. In fact, any object that complies with NSCopying can use copy. Of course, if you are sure you want to share the same mutable content, you can also use strong or retain. @property (nonatomic, copy) NSString * name; 4) weak: ARC introduces a new modifier that can replace assign and adds one more feature than assign (setting to nil, see above). Weak is used to modify ObjC objects just like strong. When using the set method to assign a value, the new value is not actually retained, nor is the old value released, only the new value is set. For example, the commonly used proxy statement
Xib control reference
5) strong: A new modifier introduced by ARC that can replace retain Please refer to retain and will not be described here. Interested readers may consider: 1) What is the correspondence between each attribute modifier and the modifiers in 3.3? 2) What is the nature of attributes? 3.5 Block Memory ManagementWhen using blocks in iOS, you must manage memory yourself. Incorrect memory management will lead to memory leaks such as circular references. Here are two points to note when declaring and using blocks under ARC: 1) If you use @property to declare a block, generally use copy to modify it (of course, you can also not write it, the compiler will automatically perform the copy operation), and try not to use retain. @property (nonatomic, copy) void(^block)(NSData * data); 2) The block will make strong references to the objects used internally, so when using it, you should make sure that it does not cause circular references. Of course, the safest way is to add a weak reference marker. __weak typeof(self) weakSelf = self; Interested readers can learn more about: 1. What is the internal implementation principle of block? 2. How many types of blocks are there in terms of memory location? How are their memory management methods? 3. How does the block manage memory for different types of external variables? 4 Classic memory leaks and their solutionsAlthough ARC has many benefits, it cannot avoid memory leaks. The following introduces common memory leaks in ARC. 4.1 Zombie Objects and Wild PointersZombie object: an object whose memory has been reclaimed. Wild pointer: A pointer to a zombie object. Sending a message to a wild pointer will cause a crash. Wild pointer errors usually appear in Xcode as: Thread 1: EXC_BAD_ACCESS, because you accessed a piece of memory that no longer belongs to you. Example code: (Run the code several times without errors, because the result of getting the pointer to a wild pointer is uncertain)
Running results:
As you can see, it crashes when it runs to line 6, and gives a prompt of EXC_BAD_ACCESS. Solution: After the object has been released, its pointer should be set to a null pointer (there is no pointer pointing to any object, and sending a message to a null pointer will not result in an error). However, when encountering EXC_BAD_ACCESS errors in actual development, it is often difficult to locate the error point. Fortunately, Xcode provides convenient tools for us to locate and analyze errors. 1) In product-scheme-edit scheme-diagnostics, check enable zombie objects. Next time this error occurs, you can locate it accurately. Running results:
As you can see, when it runs to the sixth line, it does not crash and gives a prompt of NSZombie. 2) In Xcode - open developer tool - Instruments, open the toolset and select the Zombies tool to detect zombie objects in installed applications. 4.2 Circular ReferencesCircular references are the most common problem in ARC. Some possible causes of circular references are mentioned in the previous article iOS Summary: Common Problems Affecting Normal Release of Controllers. You can take a look at it. Generally speaking, circular references can also be detected using tools, which can be divided into two types: 1) Use static analysis in product-Analyze to detect possible circular reference issues in the code. 2) In Xcode - open developer tool - Instruments, open the toolset and select the Leaks tool to detect memory leaks in installed applications. This tool can detect memory leaks that will not be prompted by static analysis but will only appear at runtime. Although the Leaks tool is powerful, it cannot detect memory leaks caused by block circular references. In this case, you usually need to troubleshoot the problem yourself (it's time to test your basic skills). The fool-proof solution is of course to rewrite the object's dealloc method to monitor whether the object is released normally to confirm that no circular reference is formed. Since the probability of circular references in ARC is relatively high, many experts or teams have provided many ideas and methods to solve this problem, and even developed plug-ins and class libraries to help developers better detect problems. Interested readers can study it and see whether it is easy to use. It is up to the readers to judge whether it is good or bad. 4.3 Objects in loops take up large amounts of memoryThis problem often occurs when the number of loops is large and the objects generated by the loop body occupy a large amount of memory. Example code: I need 10,000 actors to fight
A large number of temporary objects are generated in this loop and are not released until the end of the loop. This may cause memory leaks. The solution is similar to the common problem of automatic release pool mentioned above: create your own autoReleasePool in the loop, release temporary variables that occupy large amounts of memory in a timely manner, and reduce the peak memory usage.
However, sometimes autoReleasePool is not perfect: For example: If there are 2,000 pictures, each about 1 MB, and you need to get the size of all the pictures, what would you do? If you do this
Using the imageNamed method to load images occupies the cache memory, and autoReleasePool cannot release it. Another solution is needed for this problem. Of course, the safest solution is to do both.
4.4 *** CycleThis is a more extreme situation than 4.3. No matter what your reason is, when you start a * loop, ARC will assume that the method will not be executed to completion, the objects in the method will never be released, the memory will increase sharply, and memory leaks will occur. example:
The output is
As you can see, the cycle continues after the controller is released. What is the solution to this kind of problem? I'll leave it to the readers to think about~ ^_^ Tip: Solutions include autoreleasepool, block, timer, etc. postscriptThere are many knowledge points about iOS memory management. If we expand on them, the knowledge points covered in this article can be written into a long article. Therefore, this article is just an overview, trying to serve as a starting point to help iOS development beginners understand memory management faster. Regarding the fourth point "Classic memory leaks and their solutions", I will write a special article to introduce it in detail based on this article (with pictures and text), so stay tuned. |
<<: The easiest way to realize Web virtual reality - A-Frame framework
>>: The starting gun for VR has been fired, are you still waiting and watching?
The concept of "organizing an event" ma...
“What to give as a Valentine’s Day gift” is an an...
Blueberry yam, candied yam, steamed yam, fried ya...
Whether it is an Android phone or an IOS phone, t...
Every kind of life has its own color, and the evo...
Is it true that schools in Shanghai will start on...
According to foreign media reports, after a one o...
The Pegasus program began in the spring of 1987 a...
As we all know, China's Tang Sancai pottery, ...
Social media marketing has become the standard fo...
This summer, city walking (also known as: Citywal...
Follow "Body Code Decoding Bureau" (pub...