iOS Development ARC Memory management

Source: Internet
Author: User
Tags modifiers

The main content of this article:

    • The nature of Arc
    • Opening and closing of Arc
    • The arc modifier
    • ARC and Block
    • Arc and toll-free bridging
The nature of Arc

ARC is the compiler (time) attribute, not the runtime attribute, and not the garbage collector (GC).

Automatic Reference Counting (ARC) is a compiler-level feature that simplifies the process of managing object lifetimes (M Emory Management) in Cocoa applications.

ARC is only an improvement over the MRC (Manual Reference counting or non-arc, which we will always use MRC to refer to for non-arc management), but it is essentially indistinguishable from the previous technology. Specific information can be found in the arc compiling organ-side documents.

Opening and closing of Arc

Unlike XCode4 you can choose to close ARC,XCODE5 when creating a project, the project created is the default open arc, and there is no option to turn off arc.

If you need to turn arc on or off for a specific file, you can select targets, Compile phases, Compile Sources in the project options to find the corresponding file, add flag:

    • Open Arc:-fobjc-arc
    • Close Arc:-fno-objc-arc

The arc modifier

Arc mainly provides 4 kinds of modifiers, respectively: __strong,__weak,__autoreleasing,__unsafe_unretained.

__strong

Indicates that the reference is a strong reference. Corresponds to "strong" when the property is defined. All objects are freed only if no strong reference is pointed to.

Note: If you do not add a modifier when declaring a reference, the reference will default to a strong reference. When you need to release the object that the strong reference points to, you need to set the strong reference to nil.

__weak

Represents a reference as a weak reference. Corresponds to the "weak" used when defining the property. A weak reference does not affect the release of an object, that is, if the object does not have any strong references pointing to it, even if there are 100 weak reference objects pointing to it, the object will still be freed. Fortunately, the object is released at the same time, the weak reference to it will be automatically set nil, this technique is called zeroing weak pointer. This effectively prevents the creation of invalid pointers and wild pointers. __weak is generally used in delegate relationships to prevent circular references or to decorate UI controls that are edited and generated by Interface Builder.

__autoreleasing

A reference to automatically dispose of objects in the Autorelease pool is the same as the use of the MRC era Autorelease. This modifier cannot be used when defining a property, and the property of any object should not be of type autorelease.

A common misconception is that there is no autorelease in arc because such an "auto-release" seems a bit redundant. This misunderstanding may arise from the confusion of "automatic" and "autorelease" of Arc. In fact, you just have to look at each iOS app's main.m file to know that autorelease not only well, and become more fashion: No need to manually be created, do not need to explicitly call the [drain] method to free the memory pool.

The meanings of the following two lines of code are the same.

NSString *STR = [[[NSString alloc] initwithformat:@ "hehe"] autorelease]; mrcnsstring *__autoreleasing str = [[NSString alloc] initwithformat:@ "hehe"]; ARC

Here about Autoreleasepool do not expand, detailed information can refer to official documents or other articles.

__autoreleasing in arc is primarily used in the case of parameter passing return values (out-parameters) and reference passing parameters (pass-by-reference).

__autoreleasingis used to denote arguments, is passed by reference ( id * ) and was autoreleased on return.

such as the use of commonly used Nserror:





}

(In the WriteToFile method above, the type of the error parameter is (nserror *__autoreleasing *))

Note that if your error definition is for the strong type, then the compiler will help you implicitly do the following to ensure that the parameters of the final incoming function remain a reference to the __autoreleasing type.






}

So in order to improve efficiency and avoid this situation, we generally define the error when it is (honestly =. =) declared as __autoreleasing type:

Nserror *__autoreleasing error;

Here, after adding __autoreleasing, it is equivalent to doing the following in the MRC for the return value error:

*error = [[[Nserror alloc] init] autorelease];

The object that *error points to is created, placed in the autoreleasing pool, waits for an automatic release after the end of the function, and the user of the outside error does not need to be concerned about the release of the *error point to the object.

Another point, in Arc, is that all of these pointer pointers (NSERROR * *) function parameters, if not modifiers, are identified by the compiler as __autoreleasing types by default.

For example, the following two-segment code is equivalent:

-(NSString *) dosomething: (NSNumber *) value{        //Do something  }
-(NSString *) dosomething: (NSNumber * __autoreleasing *) value{        //Do something  }

Unless you explicitly declare __strong to value, value defaults to __autoreleasing.

Finally, some classes of methods implicitly use their own autorelease pool, which is especially careful when using the __autoreleasing type.

For example, Nsdictionary's [Enumeratekeysandobjectsusingblock] method:

-(void) Loopthroughdictionary: (nsdictionary *) dict error: (Nserror *) error{    [dict enumeratekeysandobjectsusingblock:^ (ID key, id obj, BOOL *stop) {          //do stuff            if (there is some error && ER Ror! = nil)          {                *error = [Nserror errorwithdomain:@ "Myerror"? Code:1 Userinfo:nil];          }?}    ];?}

An autorelease pool is created implicitly, and the above code is actually similar to the following:

-(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"? Code:1 Userinfo:nil];              }?          }    }];    *error has been released here by Dict's autorelease pool that was created by the enumeration: (  ?}    

To be able to use *error normally, we need a temporary reference to the strong type, which is used in the enumeration block of Dict to ensure that the object that the reference points to is not released after the Dict enumeration block, the correct way is as follows:

-(void) Loopthroughdictionary: (nsdictionary *) dict error: (Nserror *) error{
__block nserror* Temperror; Plus __block guarantee can be modified within the block  
[Dict enumeratekeysandobjectsusingblock:^ (ID key, id obj, BOOL *stop)











} ?
__unsafe_unretained

Arc was introduced in iOS 5, and this modifier is primarily intended to be compatible with iOS 4 and the lower version of the device when the arc was released, because these versions of the device do not have the weak pointer system, a simple understanding of this system is what we said weak mentioned, A system that automatically sets the reference value to nil after the weak reference points to the object being disposed. This modifier corresponds to "unsafe_unretained" when defining the property, which can actually be understood as the assign of the MRC era: simply pointing the reference to the object, without any extra action, When the point object is released, it still points to the original object that was freed (the area of memory). So it's not safe.

This modifier can now be completely ignored because iOS 4 has long been out of the history stage for many years.

* Correct posture using modifiers (mode =. =

This may be a problem that many people don't know about, including me before, but it's a particular concern.

Apple's documentation explicitly reads:

You should decorate variables correctly. When using the qualifiers in an object variable declaration,

The correct format is:

ClassName * Qualifier VariableName;

According to this note, to define a weak type of nsstring reference, it should be written as:

NSString * __weak str = @ "hehe"; That's right!

And should not be:

__weak nsstring *str = @ "hehe";  Error!

I believe a lot of people, like me, have been using the same kind of wrong writing since I started with arc.

Then there is a question, since the document is said to be wrong, why does the compiler not error? The document also explains:

Other variants is technically incorrect but is "forgiven" by the compiler. To understand the issue, seehttp://cdecl.org/.

Well, it looks like Apple daddy (=. =) Considering that many people will use the wrong, so in the compiler's side of the thoughtful help us to ignore and handle the error:) Although not error, but we should still follow the correct way to use these modifiers, if you have often used the wrong wording, then see here remember not to write so, which day the compiler angry, Do not support the wrong wording, it will be depressed.

The pointer default value in the stack is nil

The default value of the pointer declared on the stack is nil, whether it is modified by strong,weak or autoreleasing. All of this type of pointer does not have to be initialized again when the nil is placed. Although good habits are the most important, this feature reduces the likelihood of a "wild pointer" appearing.

In Arc, the following code outputs NULL instead of crash:)

-(void) MyMethod {    nsstring *name;    NSLog (@ "Name:%@", name);}
ARC and Block

In the MRC era, block implicitly adds retain to objects that enter its scope (or to objects pointed to by a block-captured pointer) to ensure that the block is properly accessed when it is used to that object.

This is a lot more careful in the case of the code shown below.

Myviewcontroller *mycontroller = [[Myviewcontroller alloc] init...];/ /implicitly calls [Mycontroller retain]; causes circular reference Mycontroller.completionhandler =  ^ (nsinteger result) {   [Mycontroller Dismissviewcontrolleranimated:yes Completion:nil];}; [Self presentviewcontroller:mycontroller animated:yes completion:^{   [Mycontroller release];//Note that this is called [ Mycontroller release]; is a conventional notation in the MRC that does not solve the problem of circular referencing above}];

In this code, Mycontroller's Completionhandler calls the Mycontroller method [Dismissviewcontroller ...], At this time Completionhandler will do retain operation to Mycontroller. And we know that Mycontroller to Completionhandler also have at least one retain (generally accurate copy), then there is in memory management in the worst case: circular reference! The simple point says is: Mycontroller retain Completionhandler, and Completionhandler also retain the Mycontroller. Circular references cause both Mycontroller and completionhandler to eventually be freed. In the delegate relationship, we use weak for delegate pointers to avoid this problem.

But fortunately, the compiler will give us a warning in time to remind us of the possible types of problems that may occur:

In this case, we generally solve the following method: Give the pointer to enter the block plus a __block modifier.

This __block has two effects in the MRC era:

    • Description variable can be changed
    • Indicates that the object pointed to by the pointer does not do this implicit retain operation

If a variable is not __block, it cannot be modified within the block, but there is one exception: static variables and global variables do not need to be __block to be modified in block.

Using this method, we modify the code to solve the problem of circular referencing:

Myviewcontroller * __block Mycontroller = [[Myviewcontroller alloc] init...];/ /... mycontroller.completionhandler =  ^ (nsinteger result) {    [Mycontroller dismissviewcontrolleranimated:yes completion:nil];};/ /after normal release or retain

After the introduction of arc, there were no operations such as retain and release, and the situation changed: In any case, the __block modifier only works on the first of the above: the explanatory variable can be changed. Even with the __block modifier, a strong reference captured by block is still a strong reference. So under Arc, if we follow the MRC notation, Completionhandler has a strong reference to Mycontroller, and Mycontroller has a strong reference to Completionhandler, which is still a circular reference, No problem solved: (

So we also need to make changes to 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 here that the block end Mycontroller strong references are guaranteed};

After Completionhandler, the Mycontroller pointer is set to nil, which guarantees the release of Completionhandler to the Mycontroller strong reference. However, Mycontroller also lifted the strong reference to the Mycontroller object. This approach is too simple and rough, and in most cases we have a better approach.

The better way is to use weak. (or in order to consider iOS4 compatibility with unsafe_unretained, the specific usage and weak are the same, considering that the iOS4 device may be extinct now, this method is not mentioned here) (the essence of this method we will talk about later)

To ensure that completionhandler this block does not have a strong reference to Mycontroller, we can define a temporary weak reference weakmyviewcontroller to point to the original Mycontroller object. This weak reference is then passed into the block, which guarantees that the block is holding a weak reference to Mycontroller rather than a strong reference. So, 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 circular references, but unfortunately introduces a new problem: since the incoming completionhandler is a weak reference, the object pointed to by Mycontroller is released before Completionhandler is called. Then the Completionhandler will not function properly. In the general single-threaded environment, this problem is unlikely to occur, but to the multi-threaded environment, it is very difficult to say, so we need to continue to improve this method.

In order to ensure access to the correct mycontroller within the block, we define a new strong reference strongmycontroller within the block to point to the object that the Weakmycontroller points to, so that a strong reference is made, You can guarantee that the Mycontroller object will not be released before Completionhandler is called. So we made another change to 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 ... }};

Here, a complete solution is complete:)

Official documentation of this issue to the end of this, but may be many friends have doubts, not to say do not want block to add a strong reference to the original Mycontroller object, why is there a strong new definition in the block, this strong reference does not cause a circular reference? The key to understanding this problem is to understand the difference between a reference captured by a block and a reference defined within a block. In order to understand this problem, we need to understand some of the block's implementation principle, but because of the length of the reason, this article does not unfold here, the detailed content can refer to other articles, here is particularly recommended Tang Qi article and 2 other authors of the blog: This and this, said is relatively clear.

This assumes that you have some idea of how the block is implemented. We're straight into the subject! Note the high energy ahead (=. =

To illustrate the problem more clearly, here is an example of a simple procedure. For example, we have the following procedures:

#include <stdio.h>int main () {    int b = ten;        int *a = &b;        void (^blockfunc) () = ^ () {            int *c = A;    };        Blockfunc ();        return 1;}

In the program, a pointer to the int type, a is a variable captured by the block, and C is a variable defined within the block. After we use CLANG-REWRITE-OBJC processing, we can see the following code:

The original main function:

int main () {    int b = ten;    int *a = &b;    void (*blockfunc) () = (void (*) ()) &__main_block_impl_0 ((void *) __main_block_func_0, &__main_block_desc_0_ DATA, a);    ((Void (*) (__block_impl *)) ((__block_impl *) blockfunc)->funcptr) ((__block_impl *) blockfunc);    return 1;}

Structure of Block:

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's structure    __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;  }};

Functions that are actually executed:

static void __main_block_func_0 (struct __main_block_impl_0 *__cself) {  int *a = __cself->a;//bound by copy        int *c = A; The reference C declared in the block is declared in the function and exists on the function stack    }

We can clearly see that a and C exist in a completely different position, if the block exists on the heap (block by default on the heap under ARC), then a as a member of the block struct will naturally exist on the heap, and C in any case, always in the function stack that actually executes the code inside the block. This also leads to a completely different life cycle of two variables: C is released when the Block function is finished, and a is released only when the Block is released from the heap.

Back to our Myviewcontroller example, ibid, if we let block capture our Mycontroller reference directly, then the reference will be copied (the reference type will also be copied) as the member variable of the block exists in the heap space in which it resides. That is, a strong reference to the Mycontroller object is added to the block, which is the essential reason for the circular reference. For the Myviewcontroller example, the block structure can be understood as: (the exact structure must be different from the following, but it is certainly the following form:)

struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* desc;    Myviewcontroller * __strong Mycontroller;  Captured strong references Mycontroller    __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;  }};

Instead, we pass a weak reference weakmycontroller to the block, and then we block the structure:

struct __main_block_impl_0 {  struct __block_impl impl;  struct __main_block_desc_0* desc;    Myviewcontroller * __weak Weakmycontroller;  Captured weak references Weakmycontroller    __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;  }};

Then look at the strong reference Strongmycontroller declared in the block, although it is a strong reference, but exists in the function stack, it exists during function execution, so the Mycontroller object also exists, but when the function is executed, Strongmycontroller is destroyed, so its strong reference to the Mycontroller object is also lifted, then the block on the Mycontroller object there is no strong reference relationship! Adding the Strongmycontroller function is the way it looks:

static void __main_block_func_0 (struct __main_block_impl_0 *__cself) {  Myviewcontroller * __strong Strongmycontroller = __cself->weakmycontroller;     // ....
}

In summary, under ARC (slightly different under the MRC), the block captures the reference and the declaration within the block, whether the existence of space and the life cycle are very different, it is this difference, resulting in the way we use their differences.

The above explained all the questions mentioned earlier, I hope you can see clearly:)

Well, finally, in the arc, the memory management of the block capture object has been simplified a lot, because there is no operation such as retain and release, it is only necessary to consider the problem of circular reference. For example, there is no memory leak problem:

Testobject *aobject = [[Testobject alloc] init];    Aobject.name = @ "hehe"; self.ablock = ^ () {        NSLog (@ "Aobject ' s name =%@", aobject.name);        };

The solution we mentioned above is just a matter of circular referencing for blocks, not all block capture references have to be treated like this, so be sure to pay attention!

Arc and toll-free bridging

There is a number of data types in the Core Foundation framework and the Foundation framework which can be used Interchang eably. This capability, called toll-free bridging, means so can use the same data type as the parameter to a Core Foundation function call or as the receiver of an objective-c message.

Toll-Free briding ensures that objects of the core foundation type and objects of the objective-c type can be easily and harmoniously used in the program. Detailed information can be found in the official documentation. Here are some examples from the official documentation:

Nslocale *gbnslocale = [[Nslocale alloc] initwithlocaleidentifier:@ "EN_GB"]; Cflocaleref Gbcflocale = (cflocaleref) Gbnslocale; Cfstringref cfidentifier = Cflocalegetidentifier (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) [@ "Nsidentifier: stringbyappendingstring:nsidentifier]);//logs identifier for current locale

In the MRC era, the use of toll-free bridging was simple, since the Objective-c type of object and the object of the core foundation type were identical to the release and retain operation rules, but since Arc joined, Object memory management rules for the OBJECTIVE-C type have changed, and the core foundation is still the previous mechanism, in other words, the core Foundation does not support arc.

At this point, you have to consider a question of which rules govern the memory of an object when doing a core foundation and objective-c type conversion. Obviously, for the same object, we can't manage both rules at the same time, so here's one thing to be sure about: which objects use the rules of objective-c (that is, ARC) and which objects use the rules of the core foundation (that is, MRC). Or, to determine the ownership change in memory management after the object type has been converted.

If you cast between Objective-c and Core Foundation-style objects, you need to tell the compiler about the ownership Seman Tics of the object using either a cast (defined in objc/runtime.h ) or a CORE Foundation-style macro (defined in NSObject.h )

So Apple introduced arc after the operation of the toll-free bridging also added a corresponding method and modifier, to indicate which rules to use to manage memory, or the ownership of memory management rights.

These methods and modifiers are:

__bridge (modifier)

Just declaring type transitions, but not making changes to memory management rules.

Like what:

Cfstringref S1 = (__bridge cfstringref) [[NSString alloc] initwithformat:@ "Hello,%@!", name];

Just did nsstring to cfstringref transformation, but the management rules are not changed, still want to use objective-c type of arc to manage S1, you can not use Cfrelease () to release S1.

__bridge_retained(修饰符)Or CFBridgingRetain(函数)

Represents the shift of the pointer type while the responsibility for memory management is transferred from the original objective-c to the core Foundation, that is, converting arc to MRC.

For example, or the above example

NSString *s1 = [[NSString alloc] initwithformat:@ "Hello,%@!", name];? Cfstringref s2 = (__bridge_retained cfstringref) s1;? Does something with s2//...? Cfrelease (S2); Note To add this after the end of use

We did the conversion in the second line, when the memory management rules were changed from arc to MRC, we needed to manage S2 's memory manually, and for S1, we couldn't free memory even if we set it to nil.

Equivalent, our program can also be written as:

NSString *s1 = [[NSString alloc] initwithformat:@ "Hello,%@!", name];? Cfstringref s2 = (cfstringref) cfbridgingretain (S1);? Does something with s2//...? Cfrelease (S2); Note To add this after the end of use
__bridge_transfer(修饰符)Or CFBridgingRelease(函数)

This modifier and function, in contrast to the __bridge_retained above, indicates that the responsibility for management is transferred from the core Foundation to the OBJECTIVE-C, which will be managed from MRC to arc.

Like what:

Cfstringref result = Cfurlcreatestringbyaddingpercentescapes (...);? NSString *s = (__bridge_transfer NSString *) result;//or nsstring *s = (NSString *) cfbridgingrelease (result);? return s;

Here we give the management responsibility of result to arc to deal with, we do not need to explicitly cfrelease ().

Well, here you might notice a detail, and the 4 main modifiers in arc (__strong,__weak,... differently, the position of the modifier here is placed in front of the type, although the official documentation does not explain it, but look at the official header file to know. Little friends, remember not to write the wrong location:)

iOS Development ARC Memory management

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.