preface
Short step without thousands of miles, not small streams into rivers and seas. Learning is like rowing upstream; not to advance is to drop back. I’m a hauler who drifts from platform to platform. No more nonsense, come in to give you from shallow to deep explanation of iOS- block usage, I hope you can gain.
1. A preliminary study of block
In this section we introduce the concept of blocks with some simple examples
1.1 Declare and Use Blocks
We use the “^” operator to declare a block variable and add “; “to the end of the block definition. To represent a complete sentence (that is, to treat the entire block definition as a simple sentence introduced in the previous section, which must be followed by a semicolon), here is an example of a block:
1: int multiplier = 7 ; 2: int (^myBlock)( int ) = ^( int num) 3: { 4: return num * multiplier; 5:};Copy the code
We use the following illustration to illustrate this example:
We declare a “myBlock” variable, using the “^” symbol to indicate that it is a block.
This is the complete definition of the block, which will be assigned to the “myBlock” variable.
Indicates that “myBlock” is a block whose return value is an int.
It takes one argument and is of an integer type.
The name of this parameter is “num”.
This is the content of the block.
It is important to note that blocks can use the same range of variables as they are defined. You can imagine that in the example above both multiplier and myBlock are two variables defined in a function that are in the middle of two curly braces “{” and”} “, Since they have the same valid range, we can use the multiplier directly in blocks. Furthermore, when defining a block as a variable, we can use it as a function:
1: int multiplier = 7 ; 2: int (^myBlock)( int ) = ^( int num) 3: { 4: return num * multiplier; 5:}; 6: printf ( "%d" , myBlock( 3 )); 7: // The result will print out 21Copy the code
1.2 Directly use Block
In many cases, we do not need to declare a block as a variable. Instead, we can write the contents of the block directly inside the block where we want to use it. In the following example, the qsort_b function, which is similar to the traditional qsort_t function, But use block as its argument directly:
1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" }; Qsort_b (myCharacters, 3, 3: sizeof (char *), 4: ^(const void *l, const void *r) char *left = *( char **)l; 7: char *right = *( char **)r; 8: return strncmp (left, right, 1 ); 9: } //end 10: );Copy the code
_block variable
In general, only variables in the same scope can be read within a block and there is no way to modify any variables defined outside the block. If we want these variables to be modified within the block, we must put the __block modifier in front of them. Multipliers in blocks are read-only, so if we specify a multiplier of 7, we cannot change the multiplier in blocks. If we change the multiplier in blocks, we will create an error. Therefore, if you want to modify the multiplier in block, you must add the __block modifier to the name of the multiplier.
1: __block int multiplier = 7 ; 2: int (^myBlock)( int ) = ^( int num) 3: { 4: if (num > 5 ) 5: { 6: multiplier = 7 ; 7: } 8: else 9: { 10: multiplier = 10 ; 11: } 12: return num * multiplier; 13:};Copy the code
Block profile
Blocks provide us with a way to embed function code in a general statement. Similar concepts in other languages are called “closure”, but in keeping with objective-C conventions, we will simply call this use “Block”.
2.1 Functions of Block
A Block is an anonymous embedded function with the following features:
Can have arguments with a pattern just like a normal function.
Has a return value.
It is possible to retrieve the lexical scope state that is defined.
You can optionally change the state of the lexical scope.
Note: The lexical scope can be thought of as the block between two braces of a function that the system places into stacked memory when the program executes, and the variables declared in this block are like the local variables we hear all the time. When we say that a block can capture the state of the same lexical scope, we can imagine that the block variable is the same level of locale variables as other locale variables (in the same stack), and the contents of the block can read other locale variables at the same level.
We can copy a block, we can throw it in some other thread and use it, so basically although blocks can be used in iOS development in C/C++ development in snippet, they can be used in Objective-C, but by system definition, A block is always considered an Objective-C object.
2.2 Use time of Block
Blocks are commonly used to represent or simplify a small piece of code. They are particularly useful for creating a piece of code that executes synchronously, encapsulating a small piece of work, or serving as a callback when a job is completed.
Blocks are heavily used in the new iOS API to replace traditional delegates and callbacks, and the new API uses blocks heavily for two reasons:
The most common use of blocks in the new API is the ability to write programs that will be executed later directly in the code, passing in the parameters of the code to the function.
Local variables can be accessed. In traditional callback implementations, local variables can be accessed only if the variable is encapsulated into a structure, while blocks can be accessed directly.
3. Declare and create blocks
3.1 Reference for declaring blocks
A Block variable is a reference to a Block, and we declare it in the same way that we declare an index, except that the Block variable refers to a function, the index uses *, and the Block uses ^. Here are some legal Block declarations:
1: / * returns void, parameter is void block * / 2: void (^ blockReturningVoidWithVoidArgument) (void); 3: / * returns an integer, two parameters are integers and character types of block * / 4: int (^ blockReturningIntWithIntAndCharArguments) (int, char); 5: /* Returns void, an array of 10 blocks, each block has an integer parameter */ 6: /* Returns void, an array of 10 blocks, each block has an integer parameter */ 6: void (^arrayOfTenBlocksReturningVoidWinIntArgument[ 10 ])( int );Copy the code
3.2 Creating a Block
9: We start a block with "^" and end with ";" The following example demonstrates a block variable and then defines a block to assign to the block variable: 11: int (^oneFrom)(int); */ 13: oneFrom = ^(int anInt) 14: {15: return anInt = -1; 16:};Copy the code
3.3 Global Blocks
I can declare a global block in a file, please refer to the following example:
1: int GlobalInt = 0 ; 2: int (^getGlobalInt)( void ) = ^ ( void ) { return GlobalInt ; };Copy the code
4 Blocks and variables
In the following section we will describe the interaction between blocks and variables.
4.1 Types of variables
We can encounter variables in blocks that we would normally encounter in functions:
L A global variable or static local variable.
L Global function.
L Regional variables and parameters passed in by enclosing scope.
In addition to the above, blocks support two additional variables:
__block variables can be used within functions, and these variables can be modified within blocks.
Import constants (const imports).
In addition, blocks can use Objective-C instance variables in the implementation of methods.
The following rules apply to the use of variables in blocks:
You can access global variables and static variables in the same domain (enclosing instantiscope).
Arguments passed to a block can be accessed (in the same way as arguments passed to a function).
Regional variables in the same domain are treated as constants in the block.
Can access variables in the same domain with a __block modifier.
Locale variables declared in blocks are used in the same way as normal functions use locale variables.
The following example shows how a local variable (# 3 above) can be used:
1: int x = 123 ; 2: void (^printXAndY)( int ) = ^( int y) 3: { 4: printf ( "%d %d\n" , x, y); 5:}; 6: // will print 123 456 7: printXAndY(456); 9: 10: int x = 123; 9: 10: int x = 123; 11: void (^printXAndY)(int) = ^(int y) 12: {13: // The following line is wrong because x is a constant here and cannot be changed. 14: x = x + y; 15: printf ( "%d %d\n" , x, y); 16:};Copy the code
If you want to modify the above variable x in a block, you must add the modifier __block to the x declaration, as described in the following section.
4.2 __block type Variables
We can change a variable imported from an external block from read-only to read-write by putting the __block modifier on it, with the restriction that the variable passed in must be a fixed length variable in memory. The __block modifier cannot be used with variable length variables such as arrays of variable length. See the following example:
1: // adds a __block modifier, so it can be modified in blocks. 2: __block int x = 123 ; 3: void (^printXAndY)( int ) = ^( int y) 4: { 5: x = x + y; 6: printf ( "%d %d\n" , x, y); 7:}; 8: // will print 579 456 9: printXAndY(456); 10: //x will become 579; 12: 13: Extern NSInteger CounterGlobal; 14: static NSInteger CounterStatic; 15: { 16: NSInteger localCounter = 42 ; 17: __block char localCharacter; 18: void (^aBlock)( void ) = ^( void ) 19: { 20: ++ CounterGlobal ; // It can be accessed. 21: ++ CounterStatic ; // It can be accessed. 22: CounterGlobal = localCounter; //localCounter is immutable when a block is created. 23: localCharacter = 'a' ; // Set the localCharacter variable defined outside. 24:}; 25: ++localCounter; // Does not affect the value in the block. 26: localCharacter = 'b' ; 27: aBlock(); // Execute the contents of the block. 28: // localCharachter will change to 'a' 29:}Copy the code
4.3 Objects and Block Variables
Blocks can be used as variables in Objective-C, C++ objects, and other blocks, but since we write programs in Objective-C for the most part, we’ll cover objective-C only in this section. The other two cases are left for interested readers to delve into for themselves.
This Objective – C objects
In environments with reference-counted objects, the objective-C object’s reference count will normally be automatically increased if we refer to it in the block, but not for objects that count __block.
If we use blocks in objective-C methods, the following memory-management things need extra attention:
L For instance variables, self’s reference count is incremented by one.
L If the value of an entity variable is accessed through a variable, the reference count for only the variable is increased by 1.
The following code illustrates the above two cases, in which instanceVariable is an entity variable:
1: dispatch_async (queue, ^ {2: / / because direct access instanceVariable physical variables, so the self retain count will add 1 3: doSomethingWithObject (instanceVariable); 4:}); 5: id localVaribale = instanceVariable; 6: dispatch_async (queue, ^ {7: / / are stock value, so at this time only are retain count + 1 8: //self's return count does not increase. 9: doSomethingWithObject (localVaribale); 10:});Copy the code
5. Use the Block
In this section we will give you a preliminary introduction to how blocks can be used
5.1 Calling a Block
When a block declares itself as a variable, we can use it like a normal function. See the following two examples:
1: int (^oneFrom)( int ) = ^( int anInt) { 2: return anInt - 1 ; 3:}; 4: printf ( "1 from 10 is %d" , oneFrom( 10 )); 1 from 10 is 9 6: float (^distanceTraveled)( float , float , float ) = ^( float startingSpeed, float acceleration, float time) 7: { 8: Float distance = (startingSpeed * time) + (acceleration * time * time); 9: return distance; 10:}; Float howFar = distanceTraveled(0.0, 9.8, 1.0); 12: //howFar becomes 4.9Copy the code
In the common case of passing a block to a function as an argument, we usually use the block “inline” way.
5.2 Using blocks as arguments to a function
We can pass blocks to functions as function parameters in the same way we would normally pass parameters to functions. In this case, most of the ways we use blocks will not tend to declare blocks but will pass blocks directly inline, which is the mainstream approach in the new SDK. We will supplement the examples from the previous chapters to illustrate:
1: char *myCharacters[ 3 ] = { "TomJohn" , "George" , "Charles Condomine" }; 2: qsort_b (myCharacters, 3 , sizeof ( char *), 3: ^( const void *l, const void *r) 4: { 5: char *left = *( char **)l; 6: char *right = *( char **)r; 7: return strncmp (left, right, 1 ); } // This is the end of the block. 9:); {"Charles Condomine", "George", "TomJohn"}Copy the code
In the above example, the block itself is part of the function argument. In the next example, the block is used in the dispatch_apply function, which is defined as follows:
1: void 2: dispatch_apply( size_t iterations, dispatch_queue_t queue, void (^block)( size_t )); 3: This function submits a block to the dispatch queue to execute multiple calls, which will be returned only after all the work in the queue has been completed. This function takes three variables, and the last parameter is the block. size_t count = 10 ; 6: dispatch_queue_t queue = 7: dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ); 8: dispatch_apply (count, queue, ^( size_t i) { 9: printf ( "%u\n" , i); 10:});Copy the code
5.3 Use a Block as a method parameter
There are many ways to use blocks in the SDK, and we can pass blocks as if they were normal parameters. Here is an example of how to retrieve the desired data index from the first five data in an array:
1: NSArray *array = [NSArray arrayWithObjects: @"A" , @"B" , @"C" , @"A" , @"B" , @"Z" , @"G" , @"are" , @" Q" ,nil ]; NSSet *filterSet = [NSSet setWithObjects: @"A", @"B", @"Z", @"Q", nil]; 5: BOOL (^test)( id obj, NSUInteger idx, BOOL *stop); 5: test = ^ (id obj, NSUInteger idx, BOOL *stop) {6: test = ^ (id obj, NSUInteger idx, BOOL *stop) {7: if ([filterSet containsObject : obj]) { 10: return YES ; 11: } 12: } 13: return NO ; 14:}; 15: NSIndexSet *indexes = [array indexesOfObjectsPassingTest :test]; 16: NSLog ( @"indexes: %@" , indexes); <NSIndexSet: 0x6101FF0 >[Number of indexes: 4 (in 2 ranges), indexes: (0-1 3-4)] // The index values are 0-1 and 3-4 respectivelyCopy the code
5.4 Use methods to avoid
In the following example, a block is a local variable in a for loop, so you must avoid assigning the block to the outer declared block:
1: // This is the wrong example, do not use this syntax in programs!! 2: void dontDoThis() { 3: void (^blockArray[3])(void); For (int I = 0; i < 3; ++i) { 5: blockArray[i] = ^{ printf("hello, %d\n", i); }; 6: // Note: this block definition is valid only in the for loop. 7: } 8: } 9: void dontDoThisEither() { 10: void (^block)(void); 11: int i = random(): 12: if (i > 1000) { 13: block = ^{ printf("got i at: %d\n", i); }; 14: // Note: this block definition is valid only within two braces after an if. 15:} 16: //... 17:}Copy the code
conclusion
Today’s share so far, if you really see here, there must be a harvest, not to say the other at least is a review again, review the old and learn new, big and small is always harvest. So why not join my ** circle ** to learn. Finally shameless, beg a wave of attention, beg a wave of praise.