Technical Points of ARC memory management for iOS development

Source: Internet
Author: User
Tags response code

Technical Points of ARC memory management for iOS development
This article comes from my personal ARC Study Notes and aims to summarize the key points of the Automatic Reference Counting (Automatic Reference Counting) Memory Management Technology in iOS development in a concise manner, so it does not involve all details. This article is not a standard tutorial on using the ARC, and assumes that the reader has some knowledge and experience with the ARC. For more information about ARC, see Apple's official documentation and other online tutorials. The main content of this article is as follows: the essence of ARC the modifier of opening and closing the ARC. The essence of ARC, BlockARC, and Toll-Free BridgingARC, is the compiler (time) feature rather than the runtime feature, it is not a garbage collector (GC ). Automatic Reference Counting (ARC) is a compiler-level feature that simplifies the process of managing object lifetimes (memory management) in Cocoa applications. ARC is an improvement compared to MRC (Manual Reference Counting or non-ARC, we will always use MRC to refer to non-ARC management methods, however, it is essentially no different from the previous technology. For more information, see the official documentation of the ARC compiler. Unlike XCode4, you can choose to disable ARC when creating a project. XCode5 enables ARC by default when creating a project, and there is no option to disable ARC. If you need to enable or disable ARC for a specific file, you can select Targets-> Compile Phases-> Compile Sources in the project options, find the corresponding file in it, add flag: Open ARC: -fobjc-arc:-fno-objc-ARC the modifier arc mainly provides four modifiers: __strong ,__ weak ,__ autoreleasing, __unsafe_unretained. _ Strong indicates that the reference is strongly referenced. Corresponds to the "strong" when defining the property ". All objects are released only when there is no strong reference pointing to them. NOTE: If no modifier is added when declaring a reference, the reference will be strongly referenced by default. To release objects with strong references, you must set strong references to nil. _ Weak indicates that the reference is a weak reference. Corresponds to the "weak" used when defining the property ". Weak references do not affect the release of objects. That is, as long as the object does not have any strong references, the object will still be released even if there are 100 weak reference objects. Fortunately, when an object is released, its weak reference will be automatically set to nil. This technology is called zeroing weak pointer. This effectively prevents invalid pointers and wild pointers. _ Weak is generally used in delegate relationships to prevent circular references or to modify the UI controls that are edited and generated by the Interface Builder. _ Autoreleasing indicates that the reference of the object is automatically released in the autorelease pool, which is the same as the use of autorelease In the MRC era. This modifier cannot be used when defining a property. The property of any object should not be of the autorelease type. A common misunderstanding is that there is no autorelease in ARC, because such an "Auto Release" seems a little redundant. This misunderstanding may be due to confusion between ARC's "automatic" and autorelease "automatic. In fact, you only need to take a look at the main. the m file knows that autorelease not only exists well, but also becomes more fashion: you do not need to manually create it, and you do not need to explicitly call the [drain] method to release the memory pool. The following two lines of code have the same meaning. NSString * str = [[NSString alloc] initWithFormat: @ "hehe"] autorelease]; // MRCNSString * _ autoreleasing str = [[NSString alloc] initWithFormat: @ "hehe"]; // The autoreleasepool is not expanded here. For more information, see the official documentation or other articles. _ Autoreleasing is mainly used in ARC to pass out-parameters and pass-by-reference parameters. _ Autoreleasing is used to denote arguments that are passed by reference (id *) and are autoreleased on return. for example, the commonly used NSError: NSError * _ autoreleasing error; else if (! [Data writeToFile: filename options: NSDataWritingAtomic error: & error]) handle {NSLog (@ "Error: % @", error );} (In the writeToFile method above, the error parameter type is (NSError * _ autoreleasing *). Note that if your error is defined as strong, the compiler will help you implicitly do the following to ensure that the parameters passed into the function are still references of the _ autoreleasing type. Copy the code NSError * error; NSError * _ autoreleasing tempError = error; // The Compiler adds if (! [Data writeToFile: filename options: NSDataWritingAtomic error: & tempError]) failed {error = tempError; // The Compiler adds NSLog (@ "Error: % @", error );} to improve efficiency and avoid this situation, we usually define the code when defining an error (honestly =. =) Declared as _ autoreleasing: NSError * _ autoreleasing error; here, after _ autoreleasing is added, the error returned in MRC is equivalent to the following: * error = [[[NSError alloc] init] autorelease]; * after an error object is created, it is put into the autoreleasing pool and will be automatically released after use, users of error outside the function do not need to care about * the release of error pointing to the object. In addition, in ARC, if no modifier is added to the function parameters of the NSError pointer (NSError **), the compiler determines them as the _ autoreleasing type by default. For example, the following two pieces of code are equivalent:-(NSString *) doSomething :( NSNumber **) value {// do something}-(NSString *) doSomething :( NSNumber * _ autoreleasing *) value {// do something} Unless you explicitly declare _ strong for value, the value is _ autoreleasing by default. Finally, some class methods use their own autorelease pool implicitly. In this case, be especially careful when using the _ autoreleasing type. For example, NSDictionary's [partition] method: Copy code-(void) loopThroughDictionary :( NSDictionary *) dict error :( NSError **) error {[dict enumerateKeysAndObjectsUsingBlock: ^, BOOL * stop) {// do stuff if (there is some error & error! = Nil) {* error = [NSError errorWithDomain: @ "MyError" response code: 1 userInfo: nil] ;}}]; pipeline} the copy Code implicitly creates an autorelease pool. The above code is actually similar to: Copy code-(void) loopThroughDictionary :( NSDictionary *) dict error :( NSError **) error {[dict enumerateKeysAndObjectsUsingBlock: ^ (id key, id obj, BOOL * stop) {@ autoreleasepool // is implicitly created {if (there is some error & error! = Nil) {* error = [NSError errorWithDomain: @ "MyError" response code: 1 userInfo: nil] ;}}}]; // * the error has been enumerated by dict and the autorelease pool created over time has been released: (warn} copy the code in order to use * error normally, we need a temporary strong reference. This temporary reference is used in the enumeration Block of dict to ensure that the referenced object will not be released after the enumeration Block of dict, the correct method is as follows: Copy code-(void) loopThroughDictionary :( NSDictionary *) dict error :( NSError **) error {_ block NSError * tempError; // Add _ block to ensure that [dict enumerateKeysAnd ObjectsUsingBlock: ^ (id key, id obj, BOOL * stop) {if (there is some error) {* tempError = [NSError errorWithDomain: @ "MyError" response code: 1 userInfo: nil] ;}}] if (error! = Nil) {* error = tempError;} finally} copy the code _ unsafe_unretainedARC, which was introduced in iOS 5, this modifier is intended to be compatible with iOS 4 and earlier versions when the ARC was just released, because these versions of devices do not have weak pointer system, A simple understanding of this system is what we mentioned in weak above. It can automatically set the reference value to nil after the weak reference points to the object is released. This modifier corresponds to "unsafe_unretained" when defining the property. Actually, it can be understood as assign in the MRC era: simply pointing the reference to the object without any additional operations, when the object is released, it still points to the original released object (the memory area ). So it is very insecure. Now you can ignore this modifier because iOS 4 has been out of the historical stage for many years. * Use the correct posture of the modifier (method =. =) This may be a problem that many people do not know, including me, but it is a special issue. The apple documentation clearly states that: You should decorate variables correctly. when using qualifiers in an object variable declaration, the correct format is: ClassName * qualifier variableName; according to this description, to define a weak-type NSString reference, it should be written as follows: NSString * _ weak str = @ "hehe"; // correct! It should not be: _ weak NSString * str = @ "hehe"; // error! I believe that many people, like me, have been using the incorrect method from the very beginning. So I have doubts here. Since the document says it is incorrect, why does the compiler not report an error? The document also explains: Other variants are technically incorrect but are "forgiven" by the compiler. To understand the issue, seehttp: // cdecl.org/. Well, it seems like Apple's father (=. =) Many people may use errors, so the compiler will help us ignore and handle this error. :) although no error is reported, however, we should still use these modifiers in the correct way. If you used to write incorrectly before, remember not to write it here, and the compiler gets angry someday, we will be depressed if we do not support incorrect writing. The default value of the pointer in the stack is nil. Whether it is modified by strong, weak or autoreleasing, the default value of the pointer declared in the stack will be nil. All pointer types do not need to be set to nil during initialization. Although good habits are the most important, this feature reduces the possibility of "wild pointers. In ARC, the following code outputs null instead of crash :)-(void) myMethod {NSString * name; NSLog (@ "name: % @", name );} in the MRC era, ARC and Block implicitly Add a retain to the objects in the scope (or objects pointed to by the captured pointer of the Block, to ensure that the Block can access this object correctly. This should be more careful when the following code is displayed. Copy the code MyViewController * myController = [[MyViewController alloc] init…]; // Call [myController retain] implicitly, causing loop reference to myController. completionHandler = ^ (NSInteger result) {[myController dismissViewControllerAnimated: YES completion: nil] ;}; [self presentViewController: myController animated: YES completion :^{ [myController release]; // Note: Calling [myController release] Here is a common method in MRC, which cannot solve the problem of loop reference above}]; copying the code in this code, the completionHandler of myController calls the myController method [dismissViewController...], In this case, completionHandler will retain the myController. We know that myController has at least one retain for completionHandler (usually copy), and the worst case in memory management is loop reference! To put it simply, myController retain completionHandler, while completionHandler retain myController. Loop reference causes both myController and completionHandler to be released eventually. In the delegate relationship, weak is used for the delegate pointer to avoid this problem. Fortunately, the compiler will give us a prompt warning to remind us that this type of problem may occur: in this case, we generally use the following method to solve the problem: add a _ Block modifier to the pointer to enter the block. This _ block has two roles in the MRC era: It indicates that the variable can be changed to indicate that the object pointed to by the pointer does not perform this implicit retain operation on a variable. If no _ block is added, it cannot be modified in the Block, but here is an exception: static variables and global variables can be modified in the block without adding _ Block. Using this method, we modified the code to solve the problem of loop reference: MyViewController * _ block myController = [[MyViewController alloc] init…]; //... MyController. completionHandler = ^ (NSInteger result) {[myController dismissViewControllerAnimated: YES completion: nil] ;}; // after normal release or retain is introduced in ARC, without retain, release, and other operations, the situation has also changed: In any case, the _ block modifier takes effect only in the first one: it indicates that the variable can be changed. Even if the _ block modifier is added, a strong reference captured by the block is still a strong reference. In this way, under ARC, if we still follow the MRC writing method, completionHandler has a strong reference to myController, while myController has a strong reference to completionHandler, which is still a circular reference, the problem persists: (we need to modify the original code. In simple cases, we can write: _ block MyViewController * myController = [[MyViewController alloc] init…]; //... MyController. completionHandler = ^ (NSInteger result) {[myController dismissViewControllerAnimated: YES completion: nil]; myController = nil; // note that the removal of the strong reference of myController after the block ends }; after completionHandler, The myController pointer is set to nil, which ensures that completionHandler removes the strong reference of myController, but also removes the strong reference of myController to the myController object. This method is too simple and crude. In most cases, we have better methods. The better way is to use weak. (Or use unsafe_unretained to consider iOS4 compatibility. The specific usage is the same as that of weak. Considering that the iOS4 device may be extinct, I will not discuss this method here) (We will talk about the nature of this method later) to ensure that the completionHandler Block has no strong reference to myController, we can define a temporary weak reference weakMyViewController to point to the object of the original myController, and pass this weak reference into the Block to ensure that the Block has a weak reference for myController, rather than a strong reference. In this case, we continue to modify the code: MyViewController * myController = [[MyViewController alloc] init…]; //... MyViewController * _ weak weakMyViewController = myController; myController. completionHandler = ^ (NSInteger result) {[weakMyViewController dismissViewControllerAnimated: YES completion: nil] ;}; this solves the problem of loop reference, but unfortunately introduces a new problem: since the completionHandler is passed in is a weak reference, when the object that myController points to is released before the completionHandler is called, The completionHandler will not work normally. In a single-threaded environment, this problem is unlikely to occur, but in a multi-threaded environment, it is not easy to say, so we need to continue to improve this method. To ensure that the correct myController can be accessed in the Block, we define a new strongMyController with strong reference in the block to point to the object pointed to by weakMyController. This adds a strong reference, the myController object will not be released before the completionHandler is called. So we made another modification to the Code: copy the code MyViewController * myController = [[MyViewController alloc] init…]; //... MyViewController * _ weak weakMyController = myController; myController. completionHandler = ^ (NSInteger result) {MyViewController * strongMyController = weakMyController; if (strongMyController ){//... [strongMyController dismissViewControllerAnimated: YES completion: nil]; //...} else {// Probably nothing ...}}; copy the code here, and a complete solution will be completed. :) the official documentation's explanation of this problem is over, but many friends may have questions, it does not mean that you do not want the Block to operate on the original myControll. Does the er object add strong references? Why is a strong reference newly defined in the Block? Does this strong reference not cause circular references? The key to understanding this problem is to understand the difference between the referenced by the Block capture and the referenced defined in the Block. To understand this problem, we need to understand the implementation principles of some blocks. However, due to the length, this article will not be discussed here. For details, refer to other articles, here we particularly recommend Tang Qiao's article and the blog posts of the other two authors: both of them are clear. Let's assume that you have some knowledge about the Block implementation principle. Let's go straight to the topic! Pay attention to the Front High Energy (=. =) Here we use a simple program to illustrate the problem more clearly. For example, we have the following program: copy the Code # include <stdio. h> int main () {int B = 10; int * a = & B; void (^ blockFunc) () = ^ () {int * c = ;}; blockFunc (); return 1;} copies the pointer of the int type in the code program. a is the variable captured by the Block, and c is the variable defined in the Block. After we use clang-rewrite-objc for processing, we can see the following code: original main function: copy the code int main () {int B = 10; int * a = & B; void (* blockFunc) () = (void (*) () & __ main_block_impl_0 (void *) _ main_block_func_0, & __ main_block_desc_0_DATA, ); (void (*) (_ block_impl *) blockFunc)-> FuncPtr) (_ block_impl *) blockFunc); return 1 ;} copy the structure of the code Block: copy the code struct _ main_block_impl_0 {struct _ block_impl impl; struct _ main_block_desc _ 0 * Desc; int * a; // The captured reference a appears in the block struct. _ main_block_impl_0 (void * fp, struct _ main_block_desc_0 * desc, int * _ a, int flags = 0): a (_ a) {impl. isa = & _ NSConcreteStackBlock; impl. flags = flags; impl. funcPtr = fp; Desc = desc ;}}; copy the function actually executed by the Code: copy the code static void _ main_block_func_0 (struct _ main_block_impl_0 * _ cself) {int * a = _ cself-> a; // bound by copy int * c = a; // reference c declared in block declares in the function and exists in the function Stack} copy the code. We can clearly see that the locations of a and c are completely different. If the Block exists on the stack (under ARC, the Block is on the stack by default ), as a member of the Block structure, a naturally exists on the stack, and c is always located in the function stack where the code actually executed in the Block. This also leads to a completely different life cycle of the two variables: c will be released after the Block function is run, and, the Block is released only when it is released from the stack. Return to the example of MyViewController, as mentioned above. If we directly let the Block capture our myController reference, the reference will be copied (the reference type will also be copied) the member variables used as blocks exist in the heap space where they are located. That is, a strong reference pointing to the myController object is added to the Block, which is the essential cause of circular reference. For the example of MyViewController, the Block struct can be understood as this: (the exact struct must be different from the following, but it must also be in the following format: copy the code struct _ main_block_impl_0 {struct _ block_impl impl; struct _ main_block_desc_0 * Desc; MyViewController * _ strong myController; // The captured strong reference myController _ main_block_impl_0 (void * fp, struct _ main_block_desc_0 * desc, int * _ a, int flags = 0): a (_) {impl. isa = & _ NSConcreteStackBlock; impl. flags = flags; impl. funcPtr = fp; Desc = desc ;}}; copy the code and pass in a weak reference weakMyController to the Block. In this case, the structure of the Block is: copy the code struct _ main_block_impl_0 {struct _ block_impl impl; struct _ main_block_desc_0 * Desc; MyViewController * _ weak weakMyController; // The captured weak reference weakMyController _ invoke (void * fp, struct _ main_block_desc_0 * desc, int * _ a, int flags = 0): a (_ a) {impl. isa = & _ NSConcreteStackBlock; impl. flags = flags; impl. funcPtr = f P; Desc = desc ;}}; copy the code and check the strong reference strongMyController declared in the Block. Although it is a strong reference, it exists in the function stack. During function execution, it always exists, so the myController object also exists, but when the function execution is complete, strongMyController is destroyed, so its strong reference to the myController object is also removed, in this case, the Block does not have a strong reference relationship with the myController object! The strongMyController function is like this: copy the code static void _ main_block_func_0 (struct _ main_block_impl_0 * _ cself) {MyViewController * _ strong strongMyController = _ cself-> weakMyController ;//....} copy the Code. In summary, under the ARC (the MRC will be slightly different), the reference captured by the Block and the reference declared in the Block are completely different in both the space and lifecycle, this is also the difference, which leads to the difference in how we use them. The above explains all the problems mentioned above, and I hope you can understand them :) well, let's try again later. In ARC, the memory management of Block capturing objects has been much simplified, since retain and release operations are not available, you only need to consider the issue of circular reference. For example, there is no memory leakage: copy the code TestObject * aObject = [[TestObject alloc] init]; aObject. name = @ "hehe"; self. aBlock = ^ () {NSLog (@ "aObject's name = % @", aObject. name) ;}; copy the solution we mentioned above to generate circular references for blocks, rather than saying that all Block capture references should be handled in this way. Be sure to pay attention to it! ARC and Toll-Free BridgingThere are a number of data types in the Core Foundation framework and the Foundation framework that can be used interchangeably. this capability, called toll-free bridging, means that you can use the same data type as the parameter to a Core Foundation function call or as the caller of an Objective-C message. the Toll-Free Briding ensures convenient and harmonious use of Core Foundation objects and Objective- C type object. For details, refer to the official documentation. The following are some examples provided in the official documents: copy the code NSLocale * gbNSLocale = [[NSLocale alloc] initWithLocaleIdentifier: @ "en_GB"]; CFLocaleRef gbCFLocale = (CFLocaleRef) gbNSLocale; CFStringRef cfIdentifier = canonical (gbCFLocale); NSLog (@ "cfIdentifier: % @", (NSString *) cfIdentifier); // logs: "cfIdentifier: en_GB" CFRelease (CFLocaleRef) gbNSLocale); CFLocaleRef myCFLocale = CFLocaleCopyCurrent (); NSLocale * myNSLocale = (NSLocale *) myCFLocale; [myNSLocale autorelease]; NSString * nsIdentifier = [myNSLocale localeIdentifier]; CFShow (CFStringRef) [@ "identifier:" identifier: nsIdentifier]); // logs identifier for current locale copy code in the MRC era, because the Objective-C type objects and the Core Foundation type objects are the same release and retain operation rules, therefore, the use of Toll-Free Bridging is relatively simple, but since the addition of ARC, the Objective-C type object memory management rules have changed, while the Core Foundation is still the previous mechanism, in other words, core Foundat Ion does not support ARC. At this time, you must consider a problem. Which rule is used to manage the object memory when converting the Core Foundation and Objective-C types. Obviously, we cannot use two rules to manage the same object at the same time, so here we must determine one thing: Which objects use Objective-C (that is, ARC) the rules of which objects use the Core Foundation rules (that is, MRC. Or determine whether the ownership of memory management changes after the object type is converted. If you cast between Objective-C and Core Foundation-style objects, you need to tell the compiler about the ownership semantics of the object using either a cast (defined in objc/runtime. h) or a Core Foundation-style macro (defined inNSObject. h) So after Apple introduced the ARC, it also added corresponding methods and modifiers to the Toll-Free Bridging operation to specify the rules used to manage the memory, or the ownership of the memory management right. These methods and modifiers are: _ bridge (modifier) only declares a type change, but does not change the memory management rules. For example, CFStringRef s1 = (_ bridge CFStringRef) [[NSString alloc] initWithFormat: @ "Hello, % @! ", Name]; it only converts NSString to CFStringRef, but the management rules remain unchanged. You still need to use Objective-c arc to manage s1. You cannot use CFRelease () release s1. _ Bridge_retained (modifier) or CFBridgingRetain (function) indicates that when the pointer type is changed, the responsibility for memory management is transferred from the original Objective-C to the Core Foundation for processing. That is, convert ARC to MRC. For example, the above example NSString * s1 = [[NSString alloc] initWithFormat: @ "Hello, % @! ", Name]; required CFStringRef s2 = (_ bridge_retained CFStringRef) s1; done // do something with s2 //...  CFRelease (s2); // note that after use, we have added this conversion in the second line. At this time, the memory management rule is changed from ARC to MRC, we need to manually manage the s2 memory. For s1, we cannot release the memory even if we set it to nil. Similarly, our program can also be written as NSString * s1 = [[NSString alloc] initWithFormat: @ "Hello, % @! ", Name]; required CFStringRef s2 = (CFStringRef) CFBridgingRetain (s1); done // do something with s2 //...  CFRelease (s2); // Add the _ bridge_transfer (modifier) or CFBridgingRelease (function) after use) this modifier is opposite to the function above, which means that the management responsibility is transferred from the Core Foundation to Objective-C, that is, the management method is changed from MRC to ARC. For example, CFStringRef result = CFURLCreateStringByAddingPercentEscapes (...); optional NSString * s = (_ bridge_transfer NSString *) result; // or NSString * s = (NSString *) CFBridgingRelease (result); optional return s; here, we handed over the result management responsibility to ARC for processing, and we do not need to explicitly put CFRelease. By the way, you may notice a detail, and the four major modifiers in ARC (_ strong ,__ weak ,...) different, here the modifier is placed in front of the type. Although it is not described in the official documentation, you can see the official header file. Dear friends, remember not to write the wrong location ~ Well, the above is the main content of this article. This time we adopted a new layout, which is much more organized than we used to, and we hope everyone can see it comfortably. If there are any errors or problems in the article, you can leave a message below or send me a mail. We look forward to your reply.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.