While researching Autoreleasepool, THIS article found that my opinion was different from some articles on the Internet, so I wrote down my opinion and discussed it with others.
First, let’s look at the following two pieces of code, and you can guess what the memory usage of the two pieces of code is:
for (int i = 0; i < 20000000; i++) {
NSString *str = [[NSString alloc] initWithFormat:@"1234567890"];
}
Copy the code
for (int i = 0; i < 20000000; i++) {
NSString *str = [NSString stringWithFormat:@"1234567890"];
}
Copy the code
As a result, the first NSString memory created with initWithFormat is almost unchanged. The second loop, which creates temporary variables with stringWithFormat, causes memory inflation.
Here’s my conclusion:
Whether the loop causes memory inflation depends on whether temporary variables are added to the Autoreleasepool outside the loop. Different object creation methods and attributes lead to different results.
There are two major cases:
1. Use the alloc/new/copy/mutableCopy/init to create temporary objects
According to the regulation of the ARC (https://clang.llvm.org/docs/AutomaticReferenceCounting.html#method-families), using the above methods to create objects that can be retain object returned directly, Autoreleasepool is not entered. Therefore, the object will be released by a release added by ARC every time it goes out of scope, so memory will not rise. We can see from the compiled intermediate language that there is only storeStrong processing for the object:
2. Temporary objects created using other methods
The situation is complicated. The key point lies in autoreleaseReturnValue and retainAutoreleasedReturnValue two methods.
AutoreleaseReturnValue returned to object is created and other method, the ARC will help us to add the function of automatically, and retainAutoreleasedReturnValue is variable will add points to the create method returns the object function. Such as
NSString *str = [NSString stringWithFormat:@"1234567890"];
Compiled into intermediate language code found joined retainAutoreleasedReturnValue:
What’s the use of these two methods? According to the regulation of the ARC (https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-autoreleasereturnvalue), If these two methods come together in pairs, they optimize the object as much as possible so that it can be strongly referenced directly, avoiding autoReleasepool. Otherwise, the object will be added to autoReleasepool when it is returned after being created and waiting for autoReleasepool torelease.
So when will it be optimized?
One is when a variable pointing to an object is strongly referenced.
We can try this out by adding a Category:
Then create temporary objects in the loop again, and we see that memory does not increase:
Of course, there are some cases that are not optimized, such as creating a weak variable to reference:
Temporary variables are added to autoReleasepool and are not released at the end of the scope, resulting in memory inflation.
Now let’s go back to the NSString creation method:
NSString *str = [NSString stringWithFormat:@"1234567890"];
Student: Would this be the case where it’s not optimized?
The answer is none of the above. This is an issue left over from history. I can’t find the source code for NSString, but you can see from the assembly that stringWithFormat returns a method that calls autorelease directly.
That is to say, still use the MRC in nsstrings, without ARC generated autoreleaseReturnValue retainAutoreleasedReturnValue corresponding with outside, directly into autoreleasepool object returns. That’s why memory keeps popping until the loop ends.
Finally, do we need to add @Autoreleasepool to the loop? My point is, instead of figuring out the complexities of optimization and history, just add @Autoreleasepool.