Probe into block implementation in iOS

Source: Internet
Author: User


[0. Brief introduction of block]

Block is the ios4.0+ and Mac OS X 10.6+ introduced to the C language extension, used to implement Anonymous Functions of the feature.

In the words of Wikipedia, Block is Apple Inc. 's added feature for C, C + +, and objective-c, which enables these languages to create closures with the syntax of a class lambda expression .

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

As for closures, I think Nanyi's sentence is concise: closures are functions that can read other functions ' internal variables .

This explanation is also appropriate for block: A function defines a block that can access the internal variables of the function.

A simple block scale is as follows:

Int (^maxblock) (int, int) = ^ (int x, int y) {return x > y x:y;};

Suppose you write with a Python lambda expression that can be written like this:

f = lambda x, y:x if x > y else y

Just because of the language characteristics of Python itself, in the function body defined by DEF, it is very natural to use DEF statements to define inline functions, since these functions are essentially objects.

assume that with BNF to represent the block's Context-Independent grammar , roughly for example the following:

Block_expression  :: =  ^  block_declare  block_statementblock_declare  :: =  block_return_ Type  block_argument_listblock_return_type:: =  return_type  |  Empty block_argument_list  :: =  argument_list  |  Empty


[1. Why Block]

In addition to defining the list of parameters, the return type, block can also get the state within the lexical scope defined (for example, local variables), and under certain conditions (for example, using __block variables) can change these states. In addition, these altered states are shared among multiple blocks within the same lexical scope, and can continue to share or change these states even if the lexical scope (such as the stack expands, out of scope) is out.

In general, blocks are encapsulated in short snippets of code that can be used as work units, often for concurrent tasks, traversal, and callbacks.

For example, we can do something when we traverse Nsarray:

-(void) Enumerateobjectsusingblock: (void (^) (id obj, Nsuinteger idx, BOOL *stop)) block;

When you set stop to Yes, you jump out of the loop and do not continue to traverse.

in very many frameworks, block is increasingly used as a callback function , instead of the traditional callback method.

    • Using block as a callback function allows the program ape to write code more smoothly, without running halfway to another place to write a callback function, and sometimes to consider where the callback function fits. Block can be used to write directly to the function when calling the code, and pass it as a reference in the past, for its task to run at the end of the callback.
    • Another advantage is that block is used as a callback to directly access local variables. For example, I want to change a user's name in a batch of users, and then update the corresponding user's cell UI with a callback. At this time I need to know the corresponding user cell index, assuming the use of traditional callback method, it is necessary to take the index to the past, callback and return it back; the index of the current action cell is recorded by an external scope (this limits the name of a user only once) ; Traverse to find the appropriate user. With block, you can access the index of the cell directly.

This document mentions several applications of block:

    • Callback processing when the task is complete
    • Message Listener Callback Processing
    • Error callback Handling
    • Enumeration callback
    • View animations, transformations
    • Sort


[2. About __block_impl]

Clang provides an option for intermediate code presentation to further understand the principle of block.

Take a very easy code example:


Compile using the-REWRITE-OBJC option:


Get a copy of the Block0.cpp file that you can see in this document, such as the following code snippet:


from the name can see that this is the implementation of block, and know that block in Clang Compiler Frontend Be implemented to generate C intermediate code. Very many languages can only implement the compiler front end, generate C intermediate code, and then take advantage of the existing very many C-compiler backend.

The members of the struct can see that Flags and reserved can be skipped first, Isa the pointer indicates that block can be a nsobject, and funcptr The pointer is clearly the corresponding function pointer of block.

As a result, the magic veil of Block was unveiled.

But where does the block-related variables go? The above mentioned block can capture the state of the lexical scope (or the outer context, scope), even if it is out of the scope, can still change these states. How is this done?


[3. Implementation of a simple block]

Let's look at a block that just outputs a word.


Generate intermediate code to get fragments such as the following:


the first structure that appears is __main_block_impl_0 , can be seen to be based on where function (main function) and The sequence appears (the No. 0) is named. Assume that the global block is named according to the variable name and the occurrence sequence. The __MAIN_BLOCK_IMPL_0 includes two member variables and a constructor, each of which is the __block_impl struct and describes the descriptive information desc, followed by initialization of the block's type information and function pointers in the constructor.

The next occurrence is the __main_block_func_0 function, which is the block corresponding function body. The function accepts a __cself parameter, which is the corresponding block itself.

The following is the __MAIN_BLOCK_DESC_0 structure, where the more valuable information is the block size.

The last is the creation and invocation of block in the main function, it can be seen that running block is called a function of block itself as a parameter, this function corresponds to the block's running body .

here, the type of block is used _nsconcretestackblock to indicate that the block is in the stack. In the same way, there are _nsconcretemallocblock and _nsconcreteglobalblock.

Because block is also a nsobject, we are able to perform retain operations on it. Only when the block is passed as a callback function to the underlying framework, the underlying framework needs to copy it. For example, suppose a callback block is used as a property, not retain, but copy. We generally write blocks in the stack, and when callbacks are needed, the callback block is not already in the stack, and using the Copy property allows the block to be placed in the heap. or use block_copy() and block_release().


[4. Capture local variable]

Let's look at a block that has access to local variables.


Generate intermediate code to get fragments such as the following:


I can see that this block structure __main_block_impl_0 a lot more. member Variables I To store the local variable I used to (a value of 1024), and at this point I can see __cself The role of the parameters, similar to the This and objective-c in C + +.

Suppose we try to change the local variable i, we get an example of the following error:


the error message is very specific, both telling us that the variable is not assignable and reminding us to use __block The type identifier.

Why can't I assign a value to the variable I?

Because the local variable i and the function __main_block_func_0 in the main function are not in the same scope , only the value is passed during the call. Of course, in the above code, we can use pointers to make changes to local variables. This is only because themain function stack is not yet complete when calling __main_block_func_0, and the variable i is still in the stack. However, in very many cases, the block is passed as a number of parameters for a callback to run. Usually in these cases, when the block is run, the function stack where the definition is defined is already 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, for the auto type of local variables, it is reasonable to disagree with block changes.


[5. Modify static local variable]

We can also determine how static local variables are altered in the block's run-through pointers.

Because static local variables exist in the data segment , there is no risk of illegal storage after stack expansion.


The difference between the upper middle code fragment and the previous fragment is that the main function is passed Address of I ( &i ) , and the member I in the __main_block_impl_0 struct becomes the pointer type ( int * ).

The value is then changed by the pointer when the block is run.

Of course, global variables, static global variables can be changed in the Block runtime. More precisely, block can change the variables that are in scope when it is called (this is __main_block_func_0). For example, when a block is 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 the change?


We add the __block indicator to the INT type variable so that the variable I can be changed in the block function body.

Then look at the middle code, there will be a lot more information. The first is the corresponding structure of the __block variable:


by the first member of the __isa pointers can also know __block_byref_i_0 can also be nsobject.

The second member __forwarding points to himself, why point to himself? There is no point in pointing to yourself, only to say that sometimes there is a __block_byref_i_0 structure that needs to be pointed.

The last member is the target storage variable i.

At this point, the __MAIN_BLOCK_IMPL_0 structure is as follows:


The member variable I of __MAIN_BLOCK_IMPL_0 becomes the __block_byref_i_0 * type.

The corresponding function __main_block_func_0 such as the following:


The highlight is the __BLOCK_BYREF_I_0 pointer type variable i, which operates through its member variable __forwarding pointer and also has a member variable. :-)

The main function, for example, is as follows:


With this seemingly complex change, we can change the value of the variable i. But the problem is the same: the __BLOCK_BYREF_I_0 type variable i is still on the stack, and when the Block is run by the callback, the stack of the variable i is already expanded, what should I do?

At such a critical moment, __main_block_desc_0 stood out:


At this point, __main_block_desc_0 has two more member functions: Copy and Dispose, respectively, pointing to __main_block_copy_0 and __main_block_dispose_0.

When the block is copied onto the heap from the stack, it calls __main_block_copy_0 to copy the member variable I of the __block type from the stack to the heap, and when the Block is released, the corresponding call is __main_block_dispose_0 to release _ The member variable I of the _block type.

One will be on the stack, one on the heap, and what if the variable is manipulated on the stack and on the heap at the same time?

At this point , the role of __forwarding is manifested: when a __block variable is copied from the stack onto the heap, the __forwarding pointer in the __BLOCK_BYREF_I_0 struct on the stack also points to the structure on the heap .


/* ---------------------------------------------------------------------------------------------------- */

Would like to continue to write, the results found that the article is a bit long. Come here first.

Original link: http://blog.csdn.net/jasonblog/article/details/7756763

Jason Lee @ Hangzhou


Probe into block implementation in iOS

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.