Exploration of block implementation in IOS

Source: Internet
Author: User
Tags call back
[0. Brief introduction of block]

Block is an extension of the C language introduced by ios4.0 + and Mac OS X 10.6 + to implement the features of anonymous functions.

In Wikipedia, block is a feature that Apple Inc. adds to C, C ++, and objective-C, allowing these languages to create closures using the syntax of Lambda-like expressions.

In the apple documentation, a block is an anonymous inline collection of code, and sometimes also called a "closure ".

With regard to closures, I think Ruan Yifeng's explanation is concise and clear: closures are functions that can read internal variables of other functions.

This explanation also applies to block: A block is defined in a function, which can access the internal variables of the function.

An example of a simple block is as follows:

[CPP]
View plaincopy

  1. INT (^ maxblock) (INT, INT) = ^ (int x, int y) {return x> Y? X: Y ;};

If you use a Lambda expression in Python, you can write it as follows:

[Python]
View plaincopy

  1. F = Lambda X, Y: X if x> y else y

However, due to Python's own language features, you can naturally use def statements to define embedded functions in def-defined function bodies, because these functions are essentially objects.

If BNF is used to represent the context-independent grammar of a block, it is roughly as follows:

[CPP]
View plaincopy

  1. Block_expression: = ^ block_declare block_statement
  2. Block_declare: = block_return_type block_argument_list
  3. Block_return_type: = return_type | null
  4. Block_argument_list: = argument_list | null

[1. Why block]

In addition to defining the parameter list and return type, block can also obtain the state within the defined lexical range (such as local variables) and under certain conditions (such as using_ BlockVariable) can modify these statuses. In addition, these modifiable states are shared among multiple blocks in the same lexical range, even if the lexical range (such as stack expansion and scope) exists ), you can continue to share or modify these statuses.

Generally, blocks are the encapsulation of some short code snippets and are applicable to work units. They are usually used for concurrent tasks, traversal, and callback.

For example, we can do something when traversing nsarray:

[CPP]
View plaincopy

  1. -(Void) enumerateobjectsusingblock :( void (^) (id obj, nsuinteger idx, bool * Stop) block;

If you set stop to yes, the loop will jump out and the traversal will not continue.

In many frameworks, block is increasingly used as a callback function, replacing the traditional callback method.

  • Using block as the callback function can make it easier for programmers to write code without having to go to another place to write a callback function. Sometimes it is appropriate to consider where the callback function is stored. With block, you can directly write the subsequent processing code when calling a function and pass it as a parameter for callback when the task is executed.
  • Another benefit is that block is used as the callback to directly access local variables. For example, if you want to modify the name of a user in a batch of users, you can call back to update the cell UI of the user. At this time, I need to know the index of the corresponding user cell. If we adopt the traditional callback method, we need to bring the index over and then return it back during the callback; you need to record the index of the current operation cell through the external scope (this limits that only one user's name can be modified at a time); you need to traverse to find the corresponding user. With block, you can directly access the index of a cell.

This document mentions several application scenarios of Block:

  • Callback processing when the task is completed
  • Message listener callback Processing
  • Error callback Processing
  • Enumeration callback
  • View animation and Transformation
  • Sort

[2. About _ block_impl]

Clang provides intermediate code display options for us to further understand the principle of block.

Take a simple piece of code as an example:


Compile with the-rewrite-objc option:


Obtain a block0.cpp file. The following code snippet is displayed in this file:


From the name, we can see that this is the implementation of the block, and that the block is implemented at the front end of the clang compiler, and the c intermediate code can be generated. Many languages can only implement the compiler front-end, generate C intermediate code, and then use many existing C compiler backend.

From the structure, we can see that flags and reserved can be skipped first,IsaThe finger indicates that the block can be an nsobjectFuncptrThe pointer is the function pointer corresponding to the block.

As a result, the secrets of block are uncovered.

However, where are the block-related variables? As mentioned above, the block can be in the status of the capture lexical range (or the outer context and scope). Even if the block is out of this range, these statuses can still be modified. How is this done?

[3. Implementation of a simple block]

Let's first look at the block that outputs only one sentence.


Generate the intermediate code and get the following snippet:


The first struct that appears is_ Main_block_impl_0It can be seen that the name is based on the function (main function) and the occurrence sequence (0th. If it is a global block, it is named based on the variable name and the sequence that appears. _ Main_block_impl_0 contains two member variables and a constructor. The member variables are the _ block_impl struct and description information DESC, respectively, then, initialize the block type information and function pointer information in the constructor.

Next_ Main_block_func_0Function, that is, the function body corresponding to the block. This function accepts_ CselfParameter, that is, the corresponding block itself.

Next is_ Main_block_desc_0Struct. The more valuable information is the block size.

The last step is to create and call blocks in the main function. We can see thatExecute BlockIt is to call a function that uses the block itself as the parameter. This function corresponds to the execution body of the block.

Here, the block type is used_ NsconcretestackblockIndicates that the block is in the stack. Similarly, there are_ NsconcretemallocblockAnd_ Nsconcreteglobalblock.

Since the block is also nsobject, we can retain it. However, when passing a block as a callback function to the underlying framework, the underlying framework needsCopyOne copy. For example, if you use a callback block as an attribute, you cannot use retain, but copy. We usually write the block in the stack. When callback is required, the callback block is usually no longer in the stack. You can use the copy attribute to place the block in the stack. Or useBlock_copy() AndBlock_release().

[4. Capture local variable]

Let's look at the block used to access local variables.


Generate the intermediate code and get the following snippet:


We can see that the block struct _ main_block_impl_0 has moreMember variablesIIs used to store the used local variable I (the value is 1024 )._ CselfThe function of the parameter is similar to this in C ++ and self in objective-C.

If we try to modify the local variable I, the following error is returned:


The error message is very detailed, indicating that variables cannot be assigned values, and reminding us to use_ BlockType identifier.

Why can't I be assigned a value?

Because the local variables I and function _ main_block_func_0 in the main function are not in the same scope, only values are passed during the call process. Of course, in the above code, we can use pointers to modify local variables. However, when _ main_block_func_0 is called, the main function Stack has not been expanded yet, and variable I is still in the stack. However, in many cases, the block is passed as a parameter for subsequent callback execution. In these cases, when a block is executed, the function stack where the block is defined has been expanded and the local variable is no longer in the stack (where is the block at this time ?), And then use the pointer to access it.

Therefore, it is reasonable to disable block modification for local variables of the auto type.

[5. Modify static local variable]

So we can also infer how static local variables are modified in the block execution body-through pointers.

Because static local variables exist in data segments, there is no risk of unauthorized access to the database after the stack is expanded.


The difference between the intermediate code snippet and the previous one is that what is passed in the main function isI address (& I)And the Member I in the _ main_block_impl_0 struct changes to the pointer type (Int
*
).

Then, when the block is executed, the value is modified through the pointer.

Of course, global variables and static global variables can be modified in the block execution body. More accurately, the block can modify the variables in the scope when it is called (here _ main_block_func_0. For example, when a block acts as a member variable, it can also access other member variables in the same object.

[6. Implementation of _ Block Variable]

So, how does the __block type variable support modification?


We add the _ block indicator to the int type variable so that variable I can be modified in the Block Function body.

Now let's look at the intermediate code, and there will be a lot more information. First, the struct corresponding to the _ Block Variable:


By the first Member_ IsaYou can also know the pointer._ Block_byref_ I _0Or nsobject.

Second Member_ ForwardingPoint to yourself. Why do you point to yourself? It doesn't make sense to point to oneself. It can only be said that sometimes it needs to point to another _ block_byref_ I _0 structure.

The last member is the target storage variable I.

In this case, the structure of __main_block_impl_0 is as follows:


The member variable I of _ main_block_impl_0 is changed_ Block_byref_ I _0 *Type.

The corresponding function _ main_block_func_0 is as follows:


The highlight is the _ block_byref_ I _0 pointer type variable I._ ForwardingPointer to operate on another member variable.

The main function is as follows:


Through this seemingly complex change, we can modify the value of variable I. But the problem also exists: __block_byref_ I _0 Type VariableIThe block is still on the stack. When the block is called back for execution, the stack where variable I is located has been expanded. What should I do?

At this critical moment, __main_block_desc_0 stands out:


In this case, _ main_block_desc_0 has two more member functions: Copy and dispose, pointing_ Main_block_copy_0And_ Main_block_dispose_0.

When a block is copied from the stack to the stack, _ main_block_copy_0 is called to copy the member variable I of the _ block type from the stack to the stack. When the block is released, _ main_block_dispose_0 is called to release the member variable I of the _ block type.

I will be on the stack later and on the stack later. What should I do if I operate the variable simultaneously on the stack and on the stack?

At this time, the role of __forwarding is embodied: When a _ block variable is copied from the stack to the stack, the _ forwarding pointer in the _ block_byref_ I _0 struct on the stack also points to the stack structure.


/* Optional /*----------------------------------------------------------------------------------------------------*/

I wanted to continue writing, and I found that the article was a little long. First come here.

Link: http://blog.csdn.net/jasonblog/article/details/7756763

Jason Lee @ Hangzhou


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.