In the previous article, we talked about the features of block and interception variables. Here we will take a look at the _ block variable.
Official reference:
You can specify that an imported variable be mutable-that is, read-write-by applying__blockStorage Type modifier.__blockStorage is similar to, but mutually exclusive of,register,auto, AndstaticStorage types for local variables.
You can read and write data by specifying the _ block storage type modifier. _ Block storage is similar, but mutually exclusive, registers, automatic variables, and local variables and static variable types (not quite understandable in the following sentence ).
Let's write an example:
void main1(){ __block char *_para1="a"; printf("init _para1:%s,%p,%p\n",_para1,_para1,&_para1); void(^testBlock)(void)=^{ printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1); }; _para1="b"; printf("before _para1:%s,%p,%p\n",_para1,_para1,&_para1); testBlock(); printf("after _para1:%s,%p,%p\n",_para1,_para1,&_para1);}
The output is as follows:
Init _ para1: A, 0x47f4, 0xbfffc9c0
Before _ para1: B, 0x4829, 0x8da4580
Exute _ para1: B, 0x4829, 0x8da4580
After _ para1: B, 0x4829, 0x8da4580
The _ block modifier is added here. The log shows that the address and value of _ para1 inside the block are the same as those of _ para1 before execution. After the block is initialized, the modification of Variable _ para1 can be synchronized to the block, the block does not intercept the variable value. Let's take a look at the CPP Code after decompilation:
// The object of the block implementation function
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};
// The parameter object referenced by the block (each parameter generates a struct)
struct __Block_byref__para1_0 { void *__isa;__Block_byref__para1_0 *__forwarding; int __flags; int __size; char *_para1;};
// Block object
struct __main1_block_impl_0 { struct __block_impl impl; struct __main1_block_desc_0* Desc; __Block_byref__para1_0 *_para1; // by ref __main1_block_impl_0(void *fp, struct __main1_block_desc_0 *desc, __Block_byref__para1_0 *__para1, int flags=0) : _para1(__para1->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};
// Block implementation
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) { __Block_byref__para1_0 *_para1 = __cself->_para1; // bound by ref printf("exute _para1:%s,%p,%p\n",(_para1->__forwarding->_para1),(_para1->__forwarding->_para1),&(_para1->__forwarding->_para1)); }
// Other functions (copy and dispose: to manage the memory of the _ block_byref _ para1_0 struct variable)
static void __main1_block_copy_0(struct __main1_block_impl_0*dst, struct __main1_block_impl_0*src) {_Block_object_assign((void*)&dst->_para1, (void*)src->_para1, 8/*BLOCK_FIELD_IS_BYREF*/);}static void __main1_block_dispose_0(struct __main1_block_impl_0*src) {_Block_object_dispose((void*)src->_para1, 8/*BLOCK_FIELD_IS_BYREF*/);}static struct __main1_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __main1_block_impl_0*, struct __main1_block_impl_0*); void (*dispose)(struct __main1_block_impl_0*);} __main1_block_desc_0_DATA = { 0, sizeof(struct __main1_block_impl_0), __main1_block_copy_0, __main1_block_dispose_0};
// This is our test function
void main1(){ __attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*)0,(__Block_byref__para1_0 *)&_para1, 0, sizeof(__Block_byref__para1_0), "a"}; printf("init _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1)); void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, 570425344); (_para1.__forwarding->_para1)="b"; printf("before _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1)); ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock); printf("after _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));}
It is found that it is not the same as without _ block. An extra struct (_ block_byref _ para1_0) is added)
struct __Block_byref__para1_0 { void *__isa;__Block_byref__para1_0 *__forwarding; int __flags; int __size; char *_para1;};
This struct is the variable declared with _ block. The code after the _ Block Variable _ para1 is converted is as follows:
__attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*)0,(__Block_byref__para1_0 *)&_para1, 0, sizeof(__Block_byref__para1_0), "a"};
In fact, it is to generate a _ block_byref _ para1_0 object, __forwarding pointer pointing to the variable struct itself
void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, 570425344);
Initialize the block and pass parameters such as the struct Variable _ para1, function address, and description information. The member _ para1 of the block object references the address of the _ para1 struct. In this way, you can modify the _ para1, in addition, modifications to the _ para1 struct can be synchronized to the member _ para1 of the block object. The modification code is as follows:
(_para1.__forwarding->_para1)="b";
Directly pass_ Block_byref _ para1_0Struct Member_ Forwarding (pointing to itself) obtains the address of the _ para1 member, and then reads and writes the address to modify the variable.
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
The block is still executed by calling the function pointer.FuncptrAnd pass the block itself. The function body code is as follows:
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) { __Block_byref__para1_0 *_para1 = __cself->_para1; // bound by ref printf("exute _para1:%s,%p,%p\n",(_para1->__forwarding->_para1),(_para1->__forwarding->_para1),&(_para1->__forwarding->_para1)); }
In the function body (_ Block_byref _ para1_0)And then get _ para1 through its member _ forwarding pointer.
My understanding is the relationship between pointer references. I don't know why I should add a _ forwarding pointer to directly reference my member _ para1. Isn't that true? Hope you can enlighten me.