[IOS learning] IX. Continued implementation of Blocks

Source: Internet
Author: User

Blocks's storage domain Block and _ block variables are essentially structured instances on the stack. For example, the __block variable is actually a struct instance of the _ block Variable on the stack. Block is also an oc object. The OC class is _ NSConcreteStackBlock. Although this class does not appear in the source code, there are many similar classes: _ NSConcreteStackBlock. Its object Block is set on the stack _ NSConcreteGlobalBlock, which is the same as the global variable, set in the data area of the Program (. data area. _ NSConcreteMallocBlock the object is set in the memory block allocated by the malloc function (HEAP)


We can see that the stack is set on the stack. For example, void (^ blk) (void) = ^ {printf ("sdfs") ;}; int main ()****
The global: impl. isa = & _ NSConcreteGlobalBlock is used for initialization;
In this case, Block uses a struct instance to set it in the data area of the program. Because automatic variables cannot be used where global variables are used, automatic variables are not intercepted. The content of the Block struct instance does not depend on the execution status, so only one instance is needed in the whole program. Therefore, you can set the Block instance with struct in the same data area as the global variable.
Only when the automatic variables are intercepted, the value intercepted by the Block using the struct instance changes according to the status during execution. As long as the Block does not intercept automatic variables, you can set the Block's struct instance in the data area of the program.
In the following cases, Block is a _ NSConcreteGlobalBlock Class Object: 1. When Block syntax is used to describe global variables, 2. When the Block syntax expression does not use the automatic variables to be intercepted
In addition, the Block generated by the Block syntax is a _ NSConcreteStackBlock Class Object and is set on the stack.
There is also a situation where the Block is set on the stack: 1. The reason why the Block exists beyond the variable scope is allocated to the block on the stack and the variable scope of the _ Block Variable ends, the Block or _ block Variable will also be discarded. However, Blocks provides a way to assign the Block and _ block variables from the stack to solve this problem. In this way, even if the syntax describes the end of its scope, the Block on the stack can still exist.


In this case, the Block assigned to the stack writes the _ NSConcreteMallocBlock Class Object to the Block, and the member variable isa; impl. isa = _ NSConcreteMallocBlock of the struct instance;
2. The _ block Variable uses the struct member Variable _ forwarding. In the above case, only the Block is used, the _ block Variable must use the struct member Variable _ forwarding to correctly access the _ block Variable no matter whether the _ block variable is configured on the stack or on the stack.
In the following example, when the _ block variable is configured on the stack, you can also access the _ block Variable on the stack. At this time, as long as the struct instance Member Variable _ forwarding on the stack points to the struct instance on the stack, both the _ block Variable on the stack and the _ block Variable on the stack can be correctly accessed.
How does Blocks copy data from stacks to stacks? When the ARC is valid, the compiler can automatically judge it. Let's look at the Block function: typedef int (^ blk_t) (int); blk_t func (int rate) {return ^ (int count) {return rate * count ;};} the Block function configured on the stack is returned. That is, during program execution, the variable scope ends when the function caller is returned from the function, so the Block on the stack is also discarded. Although there is a problem, the compilation of ARC is as follows: blk_t func (int rate) {blk_t tmp = & __ func_block_impl_0 (_ func_block_func_0, & __ func_block_desc_0_DATA, rate ); tmp = objc_retainBlock (tmp); return objc_autoreleaseReturnValue (tmp);} in this case, tmp is blk_t _ strong tmp. Here the objc_retainBlock is actually: _ Block_copy // Block generated by the Block syntax, that is, the Block configured on the stack uses a struct instance, assign a value to the tmp = _ Block_copy (tmp) variable equivalent to the Block type; // _ Block_copy function copies the Block on the stack to the stack. After copying, assign the address on the heap to the variable tmp return objc_autoreleaseReturnVlaue (tmp) as the pointer; // register the Block on the heap as the OC object to autoreleasepool and return the object.
When a Block is returned as a function return value, the compiler automatically generates code copied to the stack.
However, when we use the copy instance method, we need to manually generate code to copy the Block from the stack to the stack: copy in alloc/new/copy/mutableCopy. When we pass a Block to a method or function parameter, we need to copy it. However, if the passed parameter is properly copied in the method or function, therefore, you do not need to manually copy this method or function before calling it. For example, the Cocoa framework method and the method name contain usingBlock. Grand Central Dispatch API. Example:-(id) getBlockArray {int val = 10; return [[NSArray alloc] initWithObjects: ^ {NSLog (@ "blk0: % d", val );}, ^ {NSLog ("blk1: % d", val) ;}, nil] ;}
Id obj = getBlockArray (); typedef void (^ blk_t) (void); blk_t blk = (blk_t) [obj objectAtIndex: 0]; blk ();
At this time, blk () will cause an error because the Block on the stack is discarded when the getBlockArray function ends. Return [[NSArray alloc] initWithObjects: [^ {NSLog (@ "blk0: % d", val);} copy], [^ {NSLog (@ "blk1: % d ", val);} copy], nil];
Here, Block can directly call the copy method. Last blk = [blk copy];
What if copy is called for the Block configured on the stack and the Block configured on the data area of the program?
What if we call copy multiple times? For example, blk = [[[blk copy]; Explanation: {// Block configured on the stack, assign blk_t tmp = [blk copy] to the variable blk; // assign the Block configured on the stack to the variable tmp. The variable tmp holds the strongly referenced Block blk = tmp; // assign the variable tmp to the variable blk with the Block value, and the variable blk holds the strongly referenced Block. // Because the Block originally assigned a value is configured on the stack, it is not affected by this assignment. At this time, the holder of the Block is the variable blk and the variable tmp} // as the variable scope ends, therefore, the variable tmp is discarded. Its strong reference is invalid and the Block held by it is released. // because the Block is held by the variable blk, It is not discarded. {// The Block configured on the heap is assigned the variable blk, and the variable blk holds the strongly referenced Block blk_t tmp = [blk copy]; // The Block configured on the heap is assigned to the variable tmp. The variable tmp holds the strongly referenced Block blk = tmp; // The variable blk is assigned a value, therefore, the strong reference of the Block to be assigned is invalid and the Block is released. // The Block is not discarded because it is held by the variable tmp. // The variable blk assigns the Block of the variable tmp, and the variable blk holds the strongly referenced Block. // At this time, the holder of the Block is the variable blk and the variable tmp} // The variable tmp is discarded due to the end of the variable scope, its strong reference is invalid and the Block held by it is released // The Block is not discarded because the variable blk is still in the hold state. {Blk_t tmp = [blk copy]; blk = tmp ;}{ blk_t tmp = [blk copy]; blk = tmp ;}

_ Block Variable storage ZoneNow let's take a look at the impact of copying a block with the _ Block Variable from the stack to the stack.
If the _ Block variable is used in a block, when the Block is assigned to the heap from the stack, all the _ block variables used must be configured on the stack. These _ block variables are all copied from the stack to the stack. In this case, the Block holds the _ block variable. Even if the Block has been copied to the heap, copying the Block has no effect on the _ block variable used.
When multiple blocks use the _ Block Variable? After the first block is copied to the stack, when the remaining Block is copied to the stack, the copied Block also holds the _ block Variable and adds the reference count of the _ block variable.
If the Block is discarded, the _ block variable used by the Block is released.
The way of thinking here is exactly the same as the reference counting memory management of OC.
Now let's take a look at the reason why the _ block Variable uses the struct member Variable _ forwarding.: Whether the _ block variable is configured on the stack or on the stack, the variable can be correctly accessed. That is to say, through Block replication, the __block variable is also copied from the stack to the stack. At this time, the _ block Variable on the stack and the _ block Variable on the stack can be accessed simultaneously: _ block int val = 0; void (^ blk) (void) = [^ {+ val;} copy]; ++ val; blk (); NSLog (@ "% d", val );
We use the copy method to copy the block syntax using the _ Block variable. Both Block and _ block variables are copied from the stack to the stack. ^ {+ Val;} // use the initialized _ Block Variable on the _ block Variable stack in the block syntax expression + + val; // use Block-independent variables after Block syntax. Copy the _ block Variable on the front Stack
Conversion: ++ (val. _ forwarding-> val); when the _ block Variable on the stack is copied from the stack to the stack using the struct instance's _ block Variable, replace the value of member Variable _ forwarding with the address of the _ block Variable on the target stack to use the struct instance:
In this way, both the _ Block variable and the _ block variable used outside the syntax can smoothly access the same _ block variable in the stack or stack.

Intercepted objectBlk_t blk; {id array = [[NSMutableArray alloc] init]; blk = [^ (id obj) {[array addObject: obj]; NSLog (@ "array count = % ld", [array count]);} copy];} blk ([NSObject alloc] init]); blk ([[NSObject alloc] init]); blk ([NSObject alloc] init]);
The variable scope ends, the variable array is discarded, and the strong reference is invalid. But the code runs normally. This indicates that the NSMutableArray Class Object assigned to the variable array exists when the execution part of the last Block of the source code exceeded its variable scope. Source code after conversion: // Block struct, function struct _ main_block_impl_0 {struct _ block_impl impl; struct _ main_block_desc_0 * Desc; id _ strong array; _ main_block_impl_0 (void * fp, struct _ main_block_desc_0 * desc, id _ strong_array, int flags = 0): array (_ array) {impl. isa = & _ NSConcreteStackBlock; impl. flags = flags; impl. funcPtr = fp; Desc = desc ;}};
Static void _ main_block_func_0 (struct _ main_block_impl_0 * _ cself, id obj) {id _ strong array = _ cself-> array; [array addObject: obj]; NSLog (@ "array count = % ld", [array count]);}
Static void _ main_block_copy_0 (struct _ main_block_impl_0 * dst, struct _ blank * src) {_ Block_object_assign (& dst-> array, src-> array, BLOCK_FIELD_IS_OBJECT );}
Static void _ main_block_desc_0 {unsigned long reserved; unsigned long Block_size; void (* copy) (struct _ blocks *, struct _ main_block_impl_0 *); void (* dispose) (struct _ main_block_impl_0 *);} _ blocks = {0, sizeof (struct _ main_block_impl_0); _ main_block_copy_0, _ main_block_dispose_0 };
// Block syntax, use the Block part blk_t blk; {id _ strong array = [[NSMutableArray alloc] init]; blk = & __ main_block_impl_0 (_ main_block_func_0, & __ success, array, 0x22000000); blk = [blk copy];} (* blk-> impl. funcPtr) (blk, [[NSObject alloc] init]); (* blk-> impl. funcPtr) (blk, [[NSObject alloc] init]); (* blk-> impl. funcPtr) (blk, [[NSObject alloc] init]);
Note that the NSMutableArray class object is assigned and the automatic variable array is intercepted. It can be seen that it is a member with the _ strong modifier in the struct used by the Block: struct _ main_block_impl_0 {struct _ block_impl impl; struct _ main_block_desc_0 * Desc; id _ strong array;}; in oc, the c struct cannot contain variables with the _ strong modifier, because the compiler does not know when to initialize and discard the c struct. However, the oc Runtime Library can accurately grasp the time when the Block is copied from the stack to the heap and the Block on the stack is discarded. Therefore, we need to add copy and dispose in _ main_block_desc_0, And the _ main_block_copy_0 function assigned to the member variable as a pointer and the _ main_block_dispose_0 function.
The Block in the source code contains the object type variable array with the _ strong modifier. Therefore, you need to properly manage the object assigned to the variable array, therefore, the _ main_block_copy_0 function uses the _ Block_object_assign function to assign an object type object to the member variable array of the Block struct and hold the object. Static void _ main_block_copy_0 (struct _ main_block_impl_0 * dst, struct _ blank * src) {_ Block_object_assign (& dst-> array, src-> array, BLOCK_FIELD_IS_OBJECT );} the _ Block_object_assign function is called to assign the retain instance method. Of course, this is also true in the dispose function. _ Block_object_dispose is equivalent to the release instance method.
Let's see when the copy and dispose functions are called:

When will the Block on the stack be assigned to the heap? 1. When the Block copy instance method is called. 2. When a Block is returned as a function return value. 3. assign a Block value to a class or Block member variable with the _ strong modifier id. 4. When a Block is passed through a Cocoa framework method with usingBlock in the method name or an API of Grand Central Dispatch.
When Block copy is called, if the Block is configured on the stack, the Block will be copied from the stack to the stack. When a Block is assigned to a class or Block member variable with the _ strong modifier id type when the Block is returned as a function value, the compiler automatically takes the Block of the object as a parameter, and call the _ Block_copy function. This is the same as calling Block's copy instance method. When the Block is transferred in the Cocoa framework method containing usingBlock in the method or the Crand Central Dispatch API, in this method or function, call the Block copy instance method or the _ Block_copy function for the passed Block. That is to say, although from the source code, in the above cases, the Block on the stack is copied to the stack, but it can be attributed to the _ Block_copy function that is called to copy the Block from the stack to the stack. Instead, the dispose function is called when no one holds the Block and the Block is discarded after it is released to the stack, which is equivalent to the dealloc instance method of the object. In this way, by using an automatic variable with the _ strong modifier, the intercepted objects in the Block can exist beyond its variable scope.
The intercepted object differs from the _ block Variable: The BLOCK_FIELD_IS_OBJECT _ block Variable: BLOCK_FIELD_IS_BYREF identifies whether it is an object or _ block Variable through a flag.
However, the dispose function releases the intercepted objects, which are the same as the intercepted objects of the copy function. The copy function holds the _ block ratio used, and the dispose function releases the _ block variable used.
Therefore, the values used in the Block are assigned to the object with the _ strong modifier and the _ block Variable copied to the stack because it is held by the Block on the stack, therefore, it exists beyond its variable scope.
When using object-Type Automatic variables in a Block, we recommend that you call the Block copy instance method in the following three cases. 1. When a Block is returned as a function return value. 2. Assign the Block value to the id type or Block type member variable with the _ strong modifier of the class. 3. When a Block is transferred to the Cocoa framework method with usingBlock in the method name or the Crand Central Dispatch API.



------- 2014/3/22 Beijing

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.