Objective-C block (closure), recursion and generics

Source: Internet
Author: User

Apple expands block syntax in C, objective-C, and C ++ and supports it in gcc4.2. Now we can use it in MAC 10.6 and iOS 4. For Mac 10.6 or IOS 4.0 platforms, it is said that blocksyntax can be supported using http://code.google.com/p/plblocks.

The Grand Central Dispatch (GCD) used by Apple in snow leopard is implemented based on blocks. Grand Central Dispatch is a tool developed by Apple to help developers make it easier to take advantage of parallel processing functions of multi-core processors. For more information about blocks and GCD, see introducing blocks and Grand Central Dispatch.

You can understand it as a function pointer, anonymous function, closure, and Lambda expression. Here, block objects are used for the time being, because there are still some differences between them.

Block object

A block object is a C-level syntax and also a runtime feature. It allows you to combine function expressions and the combined results can be passed as parameters or saved, it can also be used by multiple threads. Function expressions of block objects can reference or hold local variables. In other language Environments, block objects are sometimes referred to as closure or lambda. If you want to create a work unit (code segment) that can be passed as a numerical value, you can use block objects, which provide you with more programming flexibility and more powerful functions. If you want to write a callback function or perform some operation on all the items of a group, you can also use block objects.

L declare a block

If you use block objects in inline mode, you do not need to declare them. The block object declaration syntax is similar to the function pointer declaration syntax, but the block object should use the Escape Character (^) instead of the asterisk pointer (*). The following Code declares an ablock variable that identifies a block that requires three parameters and has a float return value.

Float (^ ablock) (const int *, Int, float );

L create a block

Block uses the Escape Character (^) as the start sign, and the semicolon as the end sign. The following example declares a simple block and assigns it to the block variable (onefrom) previously declared ).

INT (^ onefrom) (INT );

Onefrom = ^ (INT anint ){

Return anint-1;

};

The semicolon at the end is the end mark of the row of Standard C. If the block expression return value is not explicitly declared, the compiler automatically derives the block content.

L Block Variable

If a local variable uses the _ Block Storage modifier, it indicates that the block should use the reference of this variable and its value can be changed. Any change to the variable only takes effect within the block syntax scope and other blocks defined in the scope.

Features of block objects

Blocks is more powerful than the lambda expression in c ++ 0x. It can be an array type:

Int main (void)

{

Void (^ P [2]) (void) = {^ (void) {puts ("Hello, world! ") ;}, ^ (Void) {puts (" goodbye! ");}};

P [0] (), P [1] ();

}

Here, the p type is void (^ [2]) (void), which indicates a variable that contains two void (^) (void) block reference elements. The following describes the accessibility of function blocks to their external temporary variables.

Static int international = 100;

Int main (void)

{

Int local = 200;

Void (^ p) (void) = ^ (void) {printf ("the answer is: % DN", global + local );};

P ();

}

Friends who are familiar with FP may think that if an external variable can be modified by a function block at will, the side effects of it cannot be easily implemented in multi-core parallel programming. Let's try it:

Static int international = 100;

Int main (void)

{

Int local = 200;

Void (^ p) (void) = ^ (void ){

Printf ("the answer is: % DN", global + local );

Global ++;

Local --; // compiling error: Error: decrement of read-only variable \ 'local \'

};

P ();

Printf ("after modified, Global is: % d and local is % DN", global, local );

}

You can modify global variables, but not local variables in the main function. If you modify the local file, the local file cannot be compiled. Apparently, blocks has a mechanism for this. So how can we modify the local?

Static int international = 100;

Int main (void)

{

_ Block int local = 200;

Static int S = 10;

Void (^ p) (void) = ^ (void ){

Printf ("the answer is: % DN", global + local );

Global ++;

S ++;

Local --;

};

P ();

Printf ("after modified, Global is: % d and local is % d and S is: % DN", global, local, S );

}

Here we introduce a new Keyword -- _ block, which declares that a local variable can be modified by the function block.

Can block be recursive?

If you want blocks to be recursive, you must be able to reference the entry address of the function block in the function block. I made some attempts. When the function block reference is global or static, that is, the value of the variable referenced by the function block in the function block is determined at the beginning, recursion can be used.

Int main (void)

{

Void (^ p) (INT) = 0;

Static void (^ const blocks) (INT) = ^ (int I) {if (I> 0) {puts ("Hello, world! "); Blocks (I-1 );}};

P = blocks;

P (2 );

}

If the static value before blocks is removed from the above Code, an error will occur during the runtime, because blocks is not initialized when referenced by the function block, therefore, the Invalid Address is accessed when you call it, or the command to be executed is undefined.

Blocks combined with generic (mm only supports fan type)

Because generics allow us to manage our code more efficiently and reasonably, they also provide a lot of convenience for componentization. So what new elements will blocks and generics combine?

Let's take a simple example:

# Import <Foundation/Foundation. h>

 

Template <void pblock (void)>

Void blocktest (void)

{

Pblock ();

}

 

Void HI (void)

{

Nslog (@ "Hi, there! ");

}

 

Int main (INT argc, const char * argv [])

{

Blocktest <HI> ();

}

Blocks hasn't appeared in the above code, but we can see that general external functions can be used as template parameters. Can blocks do this? Let's try:

# Import <Foundation/Foundation. h>

 

Template <void (^ pblock) (void)>

Void blocktest (void)

{

Pblock ();

}

 

Int main (INT argc, const char * argv [])

{

Blocktest <^ (void) {nslog (@ "Hi, there! ") ;}> ();

}

During compilation, error: no matching function for call to \ 'blocktest () \ 'is displayed on line 1 ()\'. The C ++ standard clearly states that the template parameter must be a constant expression, and if it is a function, it must be a function pointer with an external connection (that is, external-linkage. The blocks expression is not a constant expression first, and there is no external connection. Let's take a look at the second example:

# Import <Foundation/Foundation. h>

 

Template <typename T>

Void blocktest (void (& pblock) (t ))

{

Pblock (T ());

}

 

Static void HI (int)

{

Nslog (@ "the value is: % DN", );

}

 

Int main (INT argc, const char * argv [])

{

Blocktest (HI );

}

The above Code uses the function reference as the function parameter, and then deducts the template type from the real parameter type. This code can be compiled, connected, and run normally. Let's take a look at whether blocks has this generic feature:

# Import <Foundation/Foundation. h>

 

Template <typename T>

Void blocktest (void (^ pblock) (t ))

{

Pblock (T ());

}

 

Int main (INT argc, const char * argv [])

{

Blocktest (^ (int A) {nslog (@ "the value is: % DN", );});

}

After compilation, error: no matching function for call to \ 'blocktest (void (^) (INT) \ 'appears ))\'. It is useless even if the <int> template is explicitly added. That is to say, the parameter type of blocks, including the return type, cannot be a generic type. Let's look at the Third example:

# Import <Foundation/Foundation. h>

# Include <iostream>

# Include <typeinfo>

Using namespace STD;

 

Template <typename T>

Void blocktest (T pblock)

{

Pblock ();

Cout <"the type is:" <typeid (T). Name () <Endl;

}

 

Static void HI (void)

{

Nslog (@ "Hi, there! ");

}

 

Int main (INT argc, const char * argv [])

{

Blocktest (HI );

}

This code shows the real parameters of the entire function pointer type. For lambda expressions that have been implemented by many compilers, this is the only bridge linking generics. Does blocks have this feature?

# Import <Foundation/Foundation. h>

# Include <iostream>

# Include <typeinfo>

Using namespace STD;

 

Template <typename T>

Void blocktest (T pblock)

{

Pblock ();

Cout <"the type is:" <typeid (T). Name () <Endl;

}

 

Int main (INT argc, const char * argv [])

{

Blocktest (^ (void) {nslog (@ "Hi, there! ");});

}

Congratulations, we have succeeded. This code can be compiled and run properly. You can view the output results by yourself. The type information is compressed: F indicates the function, P indicates the pointer, and V indicates the void type. Blocks is the same as the lambda expression in c ++ 0x and must be a complete type. It is invalid to make the data type sharding generic. Since the specific type of C ++ 0x lambda expressions is not open to programmers, it cannot be either a template parameter or a template function parameter, however, it can use generics in template functions.

# Include <iostream>

Using namespace STD;

 

Template <typename T>

Void lambdatest (t)

{

T a = 100;

Auto ref = [a] (const T & B)-> T {return a + B ;};

Cout <"the value is:" <REF (200) <Endl;

}

Int main (void)

{

Lambdatest (short) 0 );

}

The above code can be compiled and run properly in vs2010 and Intel C ++ compiler11.0. However, it is strange that blocks does not perform well in the template function --

Template <typename T>

Void blocktest (void)

{

Void (^ pblocks) (void) = ^ {};

}

Int main (INT argc, const char * argv [])

{

Blocktest <void> ();

}

In the above Code, pblocks in the template function blocktest is useless and cannot be compiled. The error "internal error: segmentation fault" is reported. However, only the following situations can be compiled, but it does not actually make any sense:

# Import <Foundation/Foundation. h>

Template <typename T>

Void blocktest (void)

{

Void (^ pblock) (void) = nil;

}

 

Int main (INT argc, const char * argv [])

{

Blocktest <void> ();

}

It can be seen that blocks is a good Lambda expression, but it is slightly inferior to Lambda functions, especially when combined with generics. In fact, this is also related to the implementation of blocks. Blocks is still implemented dynamically as many features of objective-C, with less effort spent during compilation. The ability to use Lambda in C and objective-C is also very good.

Summary

Blocks has its own unique features in terms of scope and memory management, and has many practical functions in many scenarios. Although it is difficult to understand, but it is easy to use, I believe that using it can greatly improve code readability and efficiency.

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.