OC blocks and Java lambdas have been confused, deliberately studied OC blocks. If there is any misunderstanding or not in place, welcome to correct. In this article, we’ll take a look at the general flow of a Block from declaration to invocation.

preface

In development, we generally declare variables in the following form:

NSInteger number;
UIView *view;
Copy the code

But the Block declaration for egg pain is:

void (^block)(int number);
Copy the code

Why not this form:

void(^) (int number) block;
Copy the code

With doubt, we looked down.

C language function pointer

First, I want to show you a piece of code:

int main(int argc, char * argv[]) {
    FuncPtr is a pointer variable
    int (*funcPtr)(int count);
    // The pointer variable points to the address of the function
    funcPtr = &func;
    int count = 10;
    // Call the method
    (*funcPtr)(count);
}

// Function implementation
int func(int count) {
    int result = count++;
    NSLog(@"Count = %d, address :%p", count, &count);
    printf("result = %d\n", result);
    return result;
}
Copy the code

FuncPtr in the code above is not a function name, but a pointer variable that points to the address of the func function.

Let’s look at the declaration and implementation of blocks:

- (void)block {
    / / statement block
    void (^block)(int count);
    int count = 10;
    // The implementation of block
    block = ^void (int count) {
        count++;
        NSLog(@"count = %d, address :%p", count, &count);
    };
    // Block calls
    block(count);
    NSLog(@"count = %d, address :%p", count, &count);
}
Copy the code

We can see that the declaration of a function pointer is similar to that of a Block. There are only two differences:

  • Block has no function name
  • Block with “^”(insert token) : Since macOS and iOS APP source code use blocks extensively, insert token is easy to find so we can guess that the underlying implementation of Block is probably C function pointer?

The underlying implementation of the Block declaration

Could the underlying implementation of a block be a function pointer in C? So let’s verify that. First we create a new macOS command-line project, and then declare and call a block in the main.m function as follows:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        / / statement block
        void (^block)(int count);
        int count = 10;
        // The implementation of block
        block = ^void (int count) {
            count++;
            NSLog(@"count = %d, address :%p", count, &count);
        };
        // Block calls
        block(count);
        NSLog(@"count = %d, address :%p", count, &count);
    }
    return 0;
}
Copy the code

Use clang(LLVM compiler) to convert OC code to C/C++ code:

clang -rewrite-objc main.m
Copy the code

The main. CPP file will be created in the main.m folder. At the bottom of the file, we find the code we need:

// __block_imp structure
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

// the __main_block_desc_0 structure
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0.sizeof(struct __main_block_impl_0)};

// the __main_block_impl_0 structure
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};/ / __main_block_func_0 function
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int count) {

            count++;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_0r_hkkmpct143n4wd3xxk0l1j8c0000gn_T_main_3c0991_mi_0, count, &count);
}

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        void (*block)(int count);
        int count = 10;
        
        // The implementation of block
        block = ((void(*) (int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        
        // Block calls
        ((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, count);
        
        / / print the count
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0r_hkkmpct143n4wd3xxk0l1j8c0000gn_T_main_3c0991_mi_1, count, &count);
    }
    return 0;
}
Copy the code

Next, we’ll examine the individual constructs in the code above.

2.1 __block_impl structure
// __block_imp structure
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
Copy the code

__block_IMP can be understood as the structure of a block class object. I will describe each member of this structure. The ISA pointer indicates that a block is instantiated by _NSConcreteStackBlock, _NSConcreteGlobalBlock, or _NSConcreteMallocBlock. So, block objects have these three base classes. I’ll elaborate on these three base classes in a future article, so stay tuned. Flags Identifier. Default is 0. Reserved indicates the Reserved field. FuncPtr pointer variable, which is a function pointer to the code __main_block_func_0 function that implements the customization within the block closure.

2.2 __main_block_desc_0 structure
// the __main_block_desc_0 structure
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0.sizeof(struct __main_block_impl_0)};
Copy the code

Reserved indicates a reserved field, which is not used at present and may be used in the future. Block_size indicates how much space the structure occupies.

2.3 __main_block_IMPL_0 structure
// the __main_block_impl_0 structure
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

The __main_block_IMPL_0 structure has two members, which are Pointers to the __block_impl structure and the __main_block_DESc_0 structure. Let’s look at the __main_block_IMPL_0 constructor:

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)
Copy the code

We can see that we need to pass a pointer to FP, which is a function pointer assigned to the FuncPtr variable of the IMPL member, and the DESC structure. Fp points to the __main_block_func_0 function.

2.4 __main_block_func_0 function
/ / __main_block_func_0 function
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int count) {

            count++;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_0r_hkkmpct143n4wd3xxk0l1j8c0000gn_T_main_3c0991_mi_0, count, &count);
}
Copy the code

The __main_block_func_0 function is code that the user writes inside a block closure.


The above sections are the declaration and implementation of blocks into C/C ++ language. Let’s look at the block call inside main().

The bottom layer of Block calls

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        void (*block)(int count);
        int count = 10;
        
        // Block assignment
        block = ((void(*) (int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        
        // Block calls
        ((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, count);
        
        / / print the count
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_0r_hkkmpct143n4wd3xxk0l1j8c0000gn_T_main_3c0991_mi_1, count, &count);
    }
    return 0;
}
Copy the code

Let’s focus on the main() function above.

3.1 Block assignment
// Block assignment
block = ((void(*) (int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
Copy the code

((void (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); This code is the creation of a __main_block_IMPL_0 structure, passing in __main_block_func_0 and __main_block_desc_0_DATA. Void (^block)(int count); void (^block)(int count); . This is where function Pointers come in.

3.2 Invocation of Block
// Block calls
((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, count);
Copy the code

The FuncPtr function __main_block_func_0 calls the block function of type __block_impl *. The __main_block_func_0 function requires passing blocks of type __block_impl * and void (^block)(int count); In the count.

That’s the block inside main().

Iv. Comb the process

Let’s take a look at the flow of a block from declaration to invocation. To summarize, a block is just a pointer to a C function, with some extra structure for OC objects. The overall process is as follows:

  • Step1: declare a function pointervoid (*block)(int count);
  • Step2: create__main_block_impl_0Structure, FuncPtr refers to__main_block_func_0Function, the block pointer variable in step1 points to__main_block_impl_0The address of the
  • Step3: Call block(that is__main_block_impl_0FuncPtr(i.e__main_block_func_0) function.

Compare the above three steps to the C function pointer in Chapter 1:

FuncPtr is a pointer variable
int (*funcPtr)(int count);
// The pointer variable points to the address of the function
funcPtr = &func;
int count = 10;
// Call the method
(*funcPtr)(count);
Copy the code

This makes the flow of a block from declaration to invocation clear. So blocks are ostensibly anonymous functions, but underneath they declare a function __main_block_func_0, which has a function name.