##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 block
As 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
- define
int a = 10
Turned 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_0
In structureint a
It’s gone. There’s one more__Block_byref_a_0 *a
__main_block_func_0
In structureint a = __cself->a
Turned out to be__Block_byref_a_0 *a = __cself->a
block
Outside 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))