1. Introduction

Blocks is an extension of THE C language, which can be expressed in one sentence: anonymous functions with automatic automatic variables. You can combine functional expressions with blocks, you can pass blocks to apis, you can use blocks and store blocks in multithreaded environments. Blocks contain code that executes and the data needed during code execution. Blocks can be used as callbacks. Blocks are also known as “closures”.

2. Getting Started with Blocks

2.1 Literal and Using a Block

You can declare a block variable using the ^ operator. The block body is inside {}. The following example defines a block variable.

    int multiplier = 7;
    int (^myBlock)(int) = ^(int num){
        return num * multiplier;
    };
Copy the code

Here is some description of the code above, noting that blocks can use variable-multipliers of the same scope.When you define a block variable, you can use it as a function.

printf("%d", myBlock(3));
// prints "21"
Copy the code

2.2 Using a Block Driectly

In most cases, you don’t need to define Block Variables. You can use the literal definition of a block as an argument. The third argument to qsort_b is the literal definition of a block, without defining a block variable.

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
 
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
});
 
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
Copy the code

You can also do this by defining a block variable and taking it as the third argument to qsort_b.

char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
 
(void) (^myBlock) (const void *, const void *) =  ^(const void *l, const void *r) {
    char *left = *(char **)l;
    char *right = *(char **)r;
    return strncmp(left, right, 1);
};

qsort_b(myCharacters, 3, sizeof(char *), myBlock);
 
// myCharacters is now { "Charles Condomine", "George", "TomJohn" }
Copy the code

3. Declaring and Creating Blocks

3.1 Declare a block Reference

Block variable refers to a block as if it were a pointer to a function, but uses ^ instead of *. Below are all the block variable declarations.

void (^blockReturningVoidWithVoidArgument)(void);
int (^blockReturningIntWithIntAndCharArguments)(int, char);
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);
Copy the code

Block supports variable arguments. A Block that does not require arguments must specify void in the argument list and return void, which must also be written in code. The following example is wrong.

int (^myBlock); // The argument is empty, but void is not specified in the argument list. (^myBlock1r) (void); // The return value is null, but it does not indicate the return value is voidCopy the code

You can also use typedefs to define block variables.

typedef float (^MyBlockType)(float, float);
 
MyBlockType myFirstBlock = // ... ;
MyBlockType mySecondBlock = // ... ;
Copy the code

3.2 Creating a Block

The beginning of a block object is ^, which may be followed by a list of arguments (represented by ()), the body of the block is represented by {}, and finally the body is followed by; At the end. Define a block variable named oneFrom, as shown below. In assigning a block literal expression to it, you can also say oneFrom refers to the block. If you do not explicitly specify the return type of the block, the return type is automatically calculated based on the type of the body return expression. If return type is inferred from a return expression and the argument list is void, void can be omitted as the argument list. If there are multiple return expressions in the body, each expression must return the same type (using casting if necessary).

float (^oneFrom)(float); OneFrom = ^(float float) {float result = convertible - 1.0; return result; };Copy the code

3.3 Global Blocks

At the file level, you can define a global block variable.

#import <stdio.h>
 
int GlobalInt = 0;
int (^getGlobalInt)(void) = ^{ return GlobalInt; };
Copy the code

Blocks and Variables

This section focuses on the interaction between blocks and variables, including memory management.

Types of Variable

In block body code, variables can be divided into five categories.

  1. Global variables, including global static variables.
  2. Global function
  3. Local variables and parameters in the same scope
  4. __block variables, which are mutable within the block body and persist if blocks referencing them are copied to the heap.
  5. const imports

Here are some rules for using variables inside a block body,

  • Global variables (including static ones) are accessible
  • Global functions are accessible
  • Local variables (including static ones) declared before blocks.
  • A variable that a block can access, if allocated on the stack, defaults to its CV-qualifer as const. That is, you can’t modify it.
  • Variables inside the block body can be thought of as local variables inside a function that are copied each time a block is called.
  • be__blockModifies a nonstatic local variable that can be modified.
int GlobalVariable = 1; static int staticGlovalVariable = 2; void globalFunction(void){ printf("myBlock can assess staticGlobalFuncion\n"); } int main(int argc, const char * argv[]) { int outScopeLocalVariable = 3; static int staticOutScopeLocalVariable = 4; { static int staticLocalVariable = 5; int LocalVariable = 6; int __block blockLocalVariable = 7; //block type void (^myBlock)(int) = ^(int parameter){ printf("%d\n", GlobalVariable); printf("%d\n", staticGlovalVariable); printf("%d\n", outScopeLocalVariable); printf("%d\n", staticOutScopeLocalVariable); printf("%d\n", LocalVariable); printf("%d\n", staticLocalVariable); printf("%d\n", blockLocalVariable); printf("%d\n", parameter); globalFunction(); GlobalVariable = 1; staticGlovalVariable = 1; //outScopeLocalVariable = 1; / / do not modify staticOutScopeLocalVariable = 1; //LocalVariable = 7; // Can't modify staticLocalVariable = 1; blockLocalVariable = 1; // parameter = 1; Printf ("%d\n", GlobalVariable); printf("%d\n", staticGlovalVariable); printf("%d\n", outScopeLocalVariable); printf("%d\n", staticOutScopeLocalVariable); printf("%d\n", LocalVariable); printf("%d\n", staticLocalVariable); printf("%d\n", blockLocalVariable); printf("%d\n", parameter); }; myBlock(8); } return 0; }Copy the code

The output is as follows.

3.3 Object and Block Variables

3.3.1 Objective – C Objects

When a block is copied, when you use a calling function or method in the block, using an OC Object, it creates a strong reference to the Object to which the reference refers, depending on the situation. There are two cases, one is passing a value, the other is passing a reference. In the first case, you pass an instance variable directly, so you refer to self directly. In the second case, passing an instance variable through a value refers to the variable.

dispatch_async(queue, ^{
    // instanceVariable NSString is used by reference, a strong reference is made to self
    doSomethingWithNSString([[NSString alloc] init]);
});
 
 
NSString *localVariable = [[NSString alloc] init];
dispatch_async(queue, ^{
    /*
      localVariable is used by value, a strong reference is made to localVariable
      (and not to self).
    */
    doSomethingWithObject(localVariable);
});
Copy the code

3.3.2 rainfall distribution on 10-12 c + + Objects

If you use C++ objects in blocks, there are two things to be aware of when the block is copied.

  1. If it is a c++ stack object decorated with __block, the copy constructor of the c++ object is called.
  2. If a c++ stack object is not __block decorated, it must have a const copy constructor. The copy constructor is used when c++ objects are copied.

3.3.3 Blocks

When you reference another block inside a block, if that block is copied, the blocks inside it are copied as well. If a block variable is used inside a block, the block referenced by the block variable is also copied.

4. Others

4.1 Copying Blocks

In general, you do not need copy or retain block. Copy is required only when you need to use the block after the declared scope of the block has been destroyed, and copies the block to heap memory. You can use two C functions to copy and release blocks, respectively. To prevent memory leaks, you must balance Block_copy and Block_release operations.

Block_copy();
Block_release();
Copy the code

4.2 Patterns to Aviod

A block literal is a local data structure address on the stack that represents a block. Then its scope is in the innermost {} scope. Here are two examples of incorrect use of blocks.

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references
 
    for (int i = 0; i < 3; ++i) {
        blockArray[i] = ^{ printf("hello, %d\n", i); };
        // WRONG: The block literal scope is the "for" loop.
    }
}
 
void dontDoThisEither() {
    void (^block)(void);
 
    int i = random():
    if (i > 1000) {
        block = ^{ printf("got i at: %d\n", i); };
        // WRONG: The block literal scope is the "then" clause.
    }
    // ...
}
Copy the code

5. To summarize

  1. Block definitions and some of the wrong ways to write them
  2. Block interaction with variables and memory management.
  3. The use of __block type.
  4. OC, C++ and block Objects are used within blocks.