Use of block for iOS development, and precautions

Source: Internet
Author: User

Transferred from: http://my.oschina.net/u/1432769/blog/390401

Block is an extension of the C language, not a high-tech, and a closure or lambda expression in other languages is one thing. It is important to note that because OBJECTIVE-C does not support the GC mechanism in iOS, the use of block must manage its own memory, and memory management is the place where the block pits are used most, with incorrect memory management leading to return Cycle memory leaks or memory is released prematurely leading to crash. Block is used much like a function pointer, but the biggest difference with a function is that the block can access the values of external variables outside the function and within the lexical scope. In other words, block not only implements functions, but also carries the function's execution environment.

As you can see, the block actually contains two parts of the content

    1. Block executes the code, which is generated well at compile time;

    2. A data structure that contains all the external variable values required by the block execution. Block creates a snapshot copy of the value of the variable to be used, near the scope, to the stack.

Block differs from the function in that the block is similar to the OBJC object, which can be used to manage memory using the auto-release pool (but block is not exactly the same as the OBJC object, which is explained in detail later).

Block Basic Grammar

The basic grammar in this article does not repeat, the students self-study.

Block types of and memory management

According to the location of block in memory is divided into three types of Nsglobalblock,nsstackblock, Nsmallocblock.

    • Nsglobalblock: Similar function, in text segment;

    • Nsstackblock: In stack memory, block will be invalid after function return;

    • Nsmallocblock: In heap memory.

1, Nsglobalblock as follows, we can by whether to refer to the external variable recognition, not referencing the external variable is nsglobalblock, can be used as a function.

{

Create a Nsglobalblock

Float (^sum) (float, float) = ^ (float A, float b) {

return A + b;

};

NSLog (@ "block is%@", sum); Block is <__nsglobalblock__: 0x47d0>

}

Nsstackblock as follows:

{

Nsarray *testarr = @[@ "1", @ "2"];

void (^testblock) (void) = ^{

NSLog (@ "Testarr:%@", Testarr);

};

NSLog (@ "block is%@", ^{

NSLog (@ "Test ARR:%@", Testarr);

});

Block is <__nsstackblock__: 0xbfffdac0>

Printing can see that block is a nsstackblock, that is, on the stack, when the function returns, the block will be invalid

NSLog (@ "block is%@", testblock);

Block is <__nsmallocblock__: 0x75425a0>

The above sentence is printed in non-arc nsstackblock, but in arc it is nsmallocblock

That is, the block is copied from the stack to the heap by default in arc, and in non-arc, a manual copy is required.

}

Nsmallocblock only need to copy the Nsstackblock operation can be obtained, but retain operation will not work, will be described below

Block of the Copy , retain , Release Operation ( or is Copy paragraph)

Copy, retain, and release operations that are different from NSOBJEC:

    • Block_copy is equivalent to copy, and Block_release is equivalent to release.

    • The reference count is not changed for block retain, copy, Release Retaincount,retaincount is always 1;

    • Nsglobalblock:retain, copy, release operations are not valid;

    • Nsstackblock:retain, release operation is not valid, it must be noted that nsstackblock after the function is returned, block memory will be recycled. Even retain is useless. The easy mistake is [[Mutableaarry Addobject:stackblock], (complement: Do not worry about this in arc, because the instantiated block is copied to the heap by default in arc) after the function is out of the stack, The stackblock that were taken from the Mutableaarry have been recycled and turned into wild pointers. The correct approach is to copy the Stackblock to the heap first, then add the array: [Mutableaarry addobject:[[stackblock copy] autorelease]. A new Nsmallocblock type object is generated after Copy,copy is supported.

    • Nsmallocblock supports retain, release, although Retaincount is always 1, the memory manager will still increase and decrease the count. No new objects are generated after copy, only one reference is added, similar to retain;

    • Try not to use the retain operation on the block.

Block access management for external variables

Basic data types

1. Local Variables

Local automatic variable, read-only in block. The value of the copy variable when the block is defined is used as a constant in the block, so even if the value of the variable is changed outside the block, it does not affect his value in the block.

{

int base = 100;

Long (^sum) (int, int) = ^ long (int A, int b) {

return base + A + B;

};

base = 0;

printf ("%ld\n", sum);

The output here is 103, not 3, because the block base is a copy of the constant 100

}

2. Global variables of the static modifier

Because the address of a global variable or static variable in memory is fixed, block reads the value of the variable directly from its memory and gets the latest value, not the constant that was copied at the time of definition.

{

static int base = 100;

Long (^sum) (int, int) = ^ long (int A, int b) {

base++;

return base + A + B;

};

base = 0;

printf ("%ld\n", sum);

The output here is 4, not 103, because base is set to 0

printf ("%d\n", base);

This output is 1 because the sum will be base++.

}

3. __block Modified variables

The block variable, the variable modified by __block, is called the block variable. A block variable of the base type is equivalent to a global variable, or a static variable.

Note: When a block is used by another block, another block is copied to the heap, and the block being used is also copy. But as a parameter block, copy will not happen.

OBJC Object

Block memory management of the OBJC object is more complex, here to divide the static global local block variable analysis, but also the non-ARC and ARC analysis

Variables in non-arc

Look at a piece of code first (not arc)

@interface Myclass:nsobject {

nsobject* _instanceobj;

}

@end

@implementation MyClass

nsobject* __globalobj = nil;

-(ID) init {

if ( self = [super init]) {

_instanceobj = [[NSObject alloc] init];

}

return Self;

}

-(void) test {

Static nsobject* __staticobj = nil;

__globalobj = [[NSObject alloc] init];

__staticobj = [[NSObject alloc] init];

nsobject* localobj = [[NSObject alloc] init];

__block nsobject* blockobj = [[NSObject alloc] init];

typedef VOID (^myblock) (void);

Myblock Ablock = ^{

NSLog (@ "%@", __globalobj);

NSLog (@ "%@", __staticobj);

NSLog (@ "%@", _instanceobj);

NSLog (@ "%@", localobj);

NSLog (@ "%@", blockobj);

};

Ablock = [[Ablock copy] autorelease];

Ablock ();

NSLog (@ "%d", [__globalobj Retaincount]);

NSLog (@ "%d", [__staticobj Retaincount]);

NSLog (@ "%d", [_instanceobj Retaincount]);

NSLog (@ "%d", [Localobj Retaincount]);

NSLog (@ "%d", [Blockobj Retaincount]);

}

@end

int main (int argc, char *argv[]) {

@autoreleasepool {

myclass* obj = [[[MyClass alloc] init] autorelease];

[obj test];

return 0;

}

}

The execution result is 1 1 1 2 1.

The location of __globalobj and __staticobj in memory is deterministic, so block copy does not retain objects.

_instanceobj does not directly retain the _instanceobj object itself at block copy, but it retain self. Therefore, the _instanceobj variable can be read and written directly in block.

Localobj at block copy, the system automatically retain the object, increasing its reference count.

Blockobj is not retain at block copy.

Variable Test in Arc

Since there is no concept of retain,retaincount in arc. Only the concept of strong and weak references. When a variable has no __strong pointer pointing to it, it is released by the system. So we can test it with the code below.

Code Snippet 1 (globalobject global variable)

NSString *__globalstring = nil;

-(void) testglobalobj

{

__globalstring = @ "1";

void (^testblock) (void) = ^{

NSLog (@ "string is:%@", __globalstring); String is Http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif "alt=":("class=" Wp-smiley "> Null

};

__globalstring = Nil;

Testblock ();

}

-(void) teststaticobj

{

Static NSString *__staticstring = nil;

__staticstring = @ "1";

printf ("Static Address:%p\n", &__staticstring); Static address:0x6a8c

void (^testblock) (void) = ^{

printf ("Static Address:%p\n", &__staticstring); Static address:0x6a8c

NSLog (@ "string is:%@", __staticstring); String is Http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif "alt=":("class=" Wp-smiley "> Null

};

__staticstring = Nil;

Testblock ();

}

-(void) testlocalobj

{

NSString *__localstring = nil;

__localstring = @ "1";

printf ("Local address:%p\n", &__localstring); Local ADDRESS:0XBFFFD9C0

void (^testblock) (void) = ^{

printf ("Local address:%p\n", &__localstring); Local Address:0x71723e4

NSLog (@ "string is:%@", __localstring); String is:1

};

__localstring = Nil;

Testblock ();

}

-(void) testblockobj

{

__block NSString *_blockstring = @ "1";

void (^testblock) (void) = ^{

NSLog (@ "string is:%@", _blockstring); String is Http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif "alt=":("class=" Wp-smiley "> Null

};

_blockstring = Nil;

Testblock ();

}

-(void) testweakobj

{

NSString *__localstring = @ "1";

__weak NSString *weakstring = __localstring;

printf ("Weak address:%p\n", &weakstring); Weak address:0xbfffd9c4

printf ("Weak str address:%p\n", weakstring); Weak Str address:0x684c

void (^testblock) (void) = ^{

printf ("Weak address:%p\n", &weakstring); Weak address:0x7144324

printf ("Weak str address:%p\n", weakstring); Weak Str address:0x684c

NSLog (@ "string is:%@", weakstring); String is:1

};

__localstring = Nil;

Testblock ();

}

From the above several tests we can draw:
1. When the local variable is used, the block copies the pointer and strongly references the object pointed to by the pointer one time. Other such as global variables, static variables, block variables, and so on, block will not copy the pointer, only strong reference to the object pointed to by the pointer once.
2. The local variable is immediately marked as __weak or __unsafe_unretained. The block will still strongly reference the pointer object once. (This is not quite clear, because this writing can be avoided in the back of the problem of circular references)

Follow Ring Reference Retain cycle

Circular references refer to two objects that strongly refer to each other, that is, retain the other side, causing no one to release the memory leak problem. If you declare a delegate, you generally use assign instead of retain or strong, because once you do that, it is very likely to cause a circular reference. In previous projects, I used dynamic memory checks several times to discover the memory leaks caused by circular references.

This is about the block's circular reference problem, because when the block is copied to the heap, it retain its referenced external variables, and if the block references his host object, it is likely to cause a circular reference, such as:

-(void) dealloc

{

NSLog (@ "no cycle retain");

}

-(ID)init

{

self = [super init];

if (self) {

#if TestCycleRetainCase1

Will loop the reference

self. Myblock = ^{

[ self dosomething];

};

#elif TestCycleRetainCase2

Will loop the reference

__block Testcycleretain *weakself = self;

self. Myblock = ^{

[Weakself dosomething];

};

#elif TESTCYCLERETAINCASE3

No circular references

__weak Testcycleretain *weakself = self;

self. Myblock = ^{

[Weakself dosomething];

};

#elif TestCycleRetainCase4

No circular references

__unsafe_unretained Testcycleretain *weakself = self;

self. Myblock = ^{

[Weakself dosomething];

};

#endif

NSLog (@ "Myblock is%@", self. myblock);

}

return Self;

}

-(void) dosomething

{

NSLog (@ "do Something");

}

int main (int argc, char *argv[]) {

@autoreleasepool {

testcycleretain* obj = [[Testcycleretain alloc] init];

obj = nil;

return 0;

}

}

The above test found that after the introduction of variables with __weak and __unsafe_unretained, the Testcycleretain method can execute the Dealloc method normally, and the variables not converted and __block converted will cause circular references.
Therefore, the method of preventing circular references is as follows:
__unsafe_unretained Testcycleretain *weakself = self;

End

Add:

In manual reference counting mode, have the effect of not __block id x; retaining x . In the ARC mode, __block id x; defaults to retaining (just as all other x values). To get the manual reference counting mode behavior under ARC, you could use __unsafe_unretained __block id x; . As the name __unsafe_unretained implies, however, having a non-retained variable are dangerous (because it can dangle) and is therefore D Iscouraged. Better options __weak is to either use (if you don't need to support IOS 4 or OS X v10.6), or set the __block value to C8/>to break the retain cycle.

Use of block for iOS development, and precautions

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.