##Block technology collection

Ios-block variable interception

Block writing and use

Before reading this article, consider the following questions

  • Why can blocks intercept variables
  • Why can’t primitive data types defined outside a Block be modified inside a Block
  • Why can __block be modified inside a Block

This article will explore the underlying Block and answer the above three questions

## What is a Block anonymous function with automatic variable values

Block intercept variable

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void(^blk)(void) = ^{
            printf("Block\n");
        };
        blk();
    }
    return 0;
}
Copy the code

Compiled into CPP code, there is a lot of code, let’s simplify as follows

Struct __block_impl {void *isa; int Flags; int Reserved; void *FuncPtr; }; //2. 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; }}; //3. static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("Block\n"); } //4. 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)}; Int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); } return 0; }Copy the code

A total of four structures and a main function code block to view5. Main function code blockAs you can see, the block object is compiled__main_block_impl_0 Type, which consists of two member structures and a constructor, respectively__block_impl and__main_block_desc_0 Type, where__block_impl The structure has a function pointer that points to__main_block_func_0 Type structure, summary diagram as follows:

When a Block is defined:

((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA))
Copy the code

Block when called:

((__block_impl *)blk)->FuncPtr
Copy the code

Functions printed inside a Block are obviously placed in __main_block_func_0, so where is the intercepted data stored inside the Block? We also compile the following code

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 10;
        void(^blk)(void) = ^{
            printf(" Block\n a = %d\n", a);
        };
        blk();
    }
    return 0;
}
Copy the code

Compiled into the CPP

//1. struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int a; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int a = __cself->a; // bound by copy printf(" Block\n a = %d\n", a); } 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)}; int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; int a = 10; void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); } return 0; }Copy the code

Obviously, the __main_block_IMPL_0 structure adds a member variable int a; Int a = __cself->a is assigned to the structure’s __main_block_func_0 constructor when the Block is defined (not when the Block is called). This is how blocks intercept variables (question 1: Why blocks intercept variables). See ios-block variable interception I wrote earlier on how Block intercepts different data types

Int a = 10; int a = 10; int a = 10; int a = 10; int a = 10

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int a = 10;
        printf("before block &a = %p \n", &a);
        void(^blk)(void) = ^{
            printf(" Block\n a = %d\n in block &a = %p \n ", a, &a);
        };
        printf("after  block &a = %p \n\n", &a);
        blk();
    }
    return 0;
}
Copy the code

Print the following:

before block &a = 0x7ffeefbff4ec 
after  block &a = 0x7ffeefbff4ec 

 Block
 a = 10
 in block &a = 0x1004385f0 
Copy the code

The int a inside the block is not the same as the int A inside the block. The int A inside the block is not the int A inside the block.

So the answer to question two here is pretty obvious, why can’t the inside of the block change the outside variable, because it’s not the same variable, it just looks the same and somebody says, well, who is the a inside the block from where? Look at the __main_block_func_0 structure of the block method in the CPP code compiled earlier

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy

            printf(" Block\n a = %d\n", a);
        }
Copy the code

Int a = 10; int a = 10; int A = 10; int A = 10;

The internal a variable is redefined at the bottom of the block. The value is external (equivalent to a copy).

The __block modifier can be used to modify the source code of the main function compiled before the __block modifier is attached.

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int a = 10;
        printf("before block &a = %p \n", &a);
        void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
        printf("after  block &a = %p \n\n", &a);
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}
Copy the code

No nonsense, change the code to add __block modification, first print see

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int a = 10;
        printf("before block &a = %p \n", &a);
        void(^blk)(void) = ^{
            printf(" Block\n a = %d\n in block &a = %p \n ", a, &a);
        };
        printf("after  block &a = %p \n\n", &a);
        blk();
    }
    return 0;
}
Copy the code
before block &a = 0x7ffeefbff4e8 
after  block &a = 0x103009f98 
 Block
 a = 10
 in block &a = 0x103009f98 
Copy the code

The address of a in the __block modifier definition is inconsistent with the address inside the block and after the block is defined. When the variable is retrieved and changed outside the block, the newly generated object is retrieved

Let’s compile it

//1. struct __Block_byref_a_0 { void *__isa; __Block_byref_a_0 *__forwarding; int __flags; int __size; int a; }; //2. struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_a_0 *a; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; //3. static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_a_0 *a = __cself->a; // bound by ref printf(" Block\n a = %d\n in block &a = %p \n ", (a->__forwarding->a), &(a->__forwarding->a)); } //4. int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10}; printf("before block &a = %p \n", &(a.__forwarding->a)); void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)); printf("after block &a = %p \n\n", &(a.__forwarding->a)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); } return 0; }Copy the code

After compiling the source code to find different first

  • defineint a = 10Turned out to be__Block_byref_a_0 a = 10(compact)
  • There are many structures__Block_byref_a_0 And inside this structure there areint a
  • __main_block_impl_0In structureint aIt’s gone. There’s one more__Block_byref_a_0 *a
  • __main_block_func_0In structureint a = __cself->aTurned out to be__Block_byref_a_0 *a = __cself->a
  • blockOutside of theprintf("after block &a = %p \n\n", &a)Turned out to beprintf("after block &a = %p \n\n", &(a.__forwarding->a))

The above different translation summary is the answer: After the __block modifier is added to a variable, the variable is encapsulated as a structure, which contains variables inside the block. When modifying variables inside the block, Change the variable data inside the __Block_byref_a_0 structure (a->__forwarding->a) __Block_byref_a_0 (a.__forwarding->a)

# questions:

printf("before block &a = %p \n", &(a.__forwarding->a));
printf("after  block &a = %p \n\n", &(a.__forwarding->a));
before block &a = 0x7ffeefbff4e8 
after  block &a = 0x100474798 `
Copy the code

(a.__forwarding->a)); (a.__forwarding->a))