Let's first convert a simple block code: Source:
int main() { void (^blk)(void) = ^{printf();}; blk(); return 0; }
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};struct __main_block_ipml_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; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf();}static struct __main_block_desc_0 { unsigned long reserved; unsigned long Block_size;} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main() { void (*blk)(void) = (void (*)(void))&__main_block_impl_0( (void *)__main_block_func_0, &__main_block_desc_0_DATA); ((void (*)(struct __block_impl *))( (struct __block_impl *)blk)->FuncPtr)((struct __block_impl *)blk); return 0;}
The converted source code shows that the anonymous function used by blocks is actually processed as a simple C function. The name of the converted code is the sequence value that appears in the function according to the function name to which the block syntax belongs. Block Syntax: ^ {printf () ;}; corresponds to: static void _ main_block_func_0 (struct _ main_block_impl_0 * _ cself) {printf ();} here _ cself is equivalent
This in c ++.
Function parameter Declaration: struct _ main_block_impl_0 * _ cself and c ++ have the same this and oc self. The parameter _ cself is the pointer of the _ main_block_impl_0 struct.
struct __main_block_ipml_0 { struct __block_impl impl; struct __main_block_desc_0 *Desc;}
Here is the code for retrieving the constructor, where impl is the _ block_impl struct.
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};
From the name, we can see these flags, which provide function pointers for the areas required for future upgrades. Desc is a pointer and is a struct of _ main_block_desc_0.
static struct __main_block_desc_0 { unsigned long reserved; unsigned long Block_size;};
This is also the size of the region and block required for future upgrade.
With these struct, initialization should be performed:
__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; }
We can see that there is a _ NSConcreteStackBlock function, which is an isa Member of the _ block_impl struct initialized by the user.
How to call the constructor in main: void (* blk) (void) = (void (*) (void) & __main_block_impl_0 (void *) _ main_block_func_0, & __ main_block_desc_0_DATA );
Split:
Struct _ main_block_impl_0 tmp = _ main_block_func_0, & __ main_block_desc_0_DATA );
Struct _ main_block_impl_0 * blk = & tmp;This makes it easy to understand. The source code generates a pointer to the _ main_block_impl_0 struct instance on the stack. We assign the pointer of the _ main_block_impl_0 struct instance generated on the stack to the variable blk of the _ main_block_impl_0 struct pointer type;
Note that the parameters of the tmp function are generated. The first parameter is the C language function pointer converted by block syntax, and the second parameter is the _ main_block_desc_0 struct instance pointer initialized as a static global variable. Let's take a look at struct initialization: _ main_block_desc_0_DATA = {0, sizeof (struct _ main_block_impl_0)}; here we use the _ main_block_impl_0 struct instance size for initialization.
Let's take a look at the _ main_block_impl_0 struct instance (Block) on the stack as a graph and initialized according to these parameters:
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr;};
The struct is based on the constructor:
Isa = & _ NSConcreteStackBlock;
Flags = 0;
Reserved = 0;
FuncPtr = _ main_block_func_0;
Desc = & __ main_block_desc_0_DATA;
Next let's look at blk ();
(Void (*) (struct _ block_impl *) blk)-> FuncPtr) (struct _ block_impl *) blk ); we remove the conversion part: (* blk-> impl. funcPtr) (blk );
This is a simple way to call a function using a function pointer. The pointer to the _ main_block_func_0 function converted by the Block syntax is assigned to the member variable FuncPtr. The parameter _ cself of the _ main_block_func_0 function points to the Block value, in the source code that calls the function, we can see that the Block is passed as a parameter.
Here, isa = & _ NSConcreteStackBlock; assigns the Block pointer to the Block's struct member variable isa. Here, we need to understand the essence of the oc class and object. In fact, the so-called block is the oc object. Id is used to store oc objects. Although id can be used in the source code like void *, the id type can also be declared in c. Let's see the id:
struct objc_class { Class isa;};typedef struct objc_class *Class;typedef struct objc_object { Class isa;} *id;
Id is the pointer type of the objc_object struct.
Let's declare an oc class:
@interface MyObject :NSObject{ int val0; int val1;}@end
The object struct of this class is as follows:
struct MyObject { Class class; int val0; int val1;};
The instance variables val0 and val1 of the MyObject class are directly declared as struct members of the object. In oc, an object generated by the class means that a struct instance of the object generated by the class is generated like this struct. Each generated object, each struct instance of the object generated by this class, maintains the struct instance pointer of this class through the member variable isa.
All types of struct are the class_t struct Based on the objc_class struct. The class_t struct:
struct class_t { struct class_t *isa; struct class_t *superclass; Cache cache; IMP *vtable; uintptr_t data_NEVER_USE;};
In oc, for example, the class_t struct instance of NSObject and the class_t struct instance of NSMutablearray generate and maintain the class_t struct instance of each class. The member variables, method names, method implementations (that is, function pointers), attributes, and parent class pointers that are specifically declared by the instance and used by the oc Runtime Library.
Look at the structure just now:
struct __main_block_impl_0 { void *isa; int Flags; int Reserved; void *FuncPtr; struct __main_block_desc_0 *Desc;};
It is equivalent to the oc Class Object Structure of the objc_object structure. Isa = & NSConcreteStackBlock; that is, _ NSConcreteStackBlock is equivalent to a class_t struct instance. When a Block is processed as an oc object, information about this class is placed in _ NSConcreteStackBlock. The last Block is the OC object.
Intercepted automatic variable valueSource code for conversion through clang:
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0 *Desc; const char *fmt; int val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags = 0) : fmt(_fmt), val(_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) { const char &fmt = __cself->fmt; int val = __cself->val; printf(fmt, val);}static struct __main_block_desc_0 { unsigned long reserved; unsigned long Block_size;} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main() { int dmy = 256; int val = 10; const char *fmt = "val = %d\n"; void (*blk)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA, fmt, val); return 0;}
First, we can see that the automatic variables used in the Block syntax expression are appended to _ main_block_impl_0 as member variables: int val. If the Block syntax expression is not used, automatic variables are not appended. Blocks's automatic variable interception only applies to the automatic variables used in the Block. _ Main_block_impl_0 (void * fp, struct _ main_block_desc_0 * desc, const char * _ fmt, int _ val, int flags = 0): fmt (_ fmt ), variable is initialized during val (_ val. When initializing a struct instance, initialize the member variables appended by the automatic variables based on the parameters passed to the constructor. Through: void (* blk) (void) = & __ main_block_impl_0 (_ main_block_func_0, & __ main_block_desc_0_DATA, fmt, val ); here, the fmt and val variables used to execute the Block syntax are used to initialize the _ main_block_impl_0 struct instance.
Impl. isa = & _ NSConcreteStackBlock;
Impl. Flags = 0;
Impl. FuncPtr = _ main_block_func_0;
Desc = & __ main) block_desc_0_DATA;
Fmt = "val = % d \ n ";
Val = 10;
In this way, we know that the automatic variable value is intercepted in the _ main_block_impl_0 struct instance.
Here is the implementation of block anonymous functions: ^ {printf (fmt, val );};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { const char &fmt = __cself->fmt; int val = __cself->val; printf(fmt, val);}
We can see that the automatic variables on the member variables of the _ main_block_impl_0 struct instance are intercepted. These variables are declared and defined before the Block syntax expression. Therefore, the original source code expression can be executed using the intercepted automatic variable value without modification.
When the Block syntax is executed, the automatic variable value used by the Block syntax expression is saved to the Block struct instance.
Finally, Block cannot directly use the C language array type. Let's take a look at the source code passed by the array: void func (char a [10]) {---;} int main () {char a [10] = {1 }; func (a);} in the constructor, assign the parameter to the member variable. In this way, the member variable can be assigned to the automatic variable in the function that has changed the block Syntax: void func (char a [10]) {char B [10] = a; ---;} int main () {char a [10] = {1 }; func (a);} cannot be compiled.
_ Block Operator
In the code just now: ^ {printf (fmt, val );};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { const char &fmt = __cself->fmt; int val = __cself->val; printf(fmt, val);}
For example, an anonymous function with an automatic variable value. As mentioned, only the values of automatic variables are intercepted. When an automatic variable is used in a Block, re-writing the automatic variable in the Block's struct instance does not change the previously intercepted automatic variable. If you change the automatic variables in the Block Based on the image, the compilation error will occur. But in this way, we can no longer save the value in the block. Solution: 1. c has a variable that allows block rewriting. Static variables static global variables
In the Block, access to static global variables/global variables is not changed and can be directly used. In static variables, the converted function is originally set outside the function that contains the block syntax, so it cannot be accessed from the variable scope.
int global_val = 1;static int static_global_val = 2;int main() { static int static_val = 3; void (^blk)(void) = ^{ global_val *= 1; static_global_val *= 2; static_val *= 3; }; return 0;}
After conversion:
int global_val = 1;static int static_global_val = 2;struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *static_val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags = 0):static_val(_static_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *static_val = __cself->static_val; global_val *= 1; static_global_val *= 2; (*static_val) *= 3;}static struct __main_block_desc_0 { unsigned long reserved; unsigned long Block_size;} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};int main() { static int static_val = 3; blk = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA, &static_val); return 0;}
The global variables and static global variables are not converted, but the pointers to static variables are accessed. Pass the pointer of the static variable static_val to the constructor of the _ main_block_impl_0 struct and save it. This is the easiest way to use a variable beyond the scope.
2. Use the _ block specifier _ block to store domain class specifiers _ block storage-class-specifier c: typedef extern static auto register the _ block specifier here is similar to the static, auto, and register specifiers. They are brave enough to specify the variable value to set it to that storage area.
__block int val = 10;void (^blk)(void) = ^{val = 1;};
After compilation:
struct __Block_byref_val_0 { void *__isa; __Block_byref_val_0 *__forwarding; int __flags; int __size; int val;};struct __main_block_impl_0{ struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_val_0 *val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags = 0) : val(_val->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_val_0 *val = __cself->val; (val->__forwarding->val) = 1;}static void __main_block_copy_0( struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src) { __Block_object_assign(&dst->val, src->val, BLOCK_FIELD_IS_BYREF);}static void __main_block_dispose_0(struct __main_block_impl_0 *src) { __Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF);}static struct __main_block_desc_0 { unsigned long reserved; unsigned long Block_size; void (*copy) (struct __main_block_impl_0*, struct __main_block_impl_0*); void (*dispose)(struct __main_block_impl_0*);} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};int main() { __Block_byref_val_0 val = { 0, &val, 0, sizeof(__Block_byref_val_0), 10 }; blk = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &val, 0x22000000); return 0;}
The code is very long. How does the _ block Variable val be converted?
_ Block_byref_val_0 val = {
0,
& Val,
0,
Sizeof (_ Block_byref_val_0 ),
10
};It becomes a struct. The _ block Variable also becomes an automatic variable of the _ Block_byref_val_0 struct type like the Block variable, that is, the _ Block_byref_val_0 struct instance generated on the stack. The value is initialized to 10. This value is also present in the initialization of the struct instance:
Struct _ Block_byref_val_0 {
Void * _ isa;
_ Block_byref_val_0 * _ forwarding;
Int _ flags;
Int _ size;
Int val;
};This means that the struct holds a member variable equivalent to the original automatic variable. The val in the above struct is equivalent to the member variable of the original automatic variable.
Let's see the code for assigning values to the _ block Variable: ^ {val = 1;} After conversion:
Static void _ main_block_func_0 (struct _ main_block_impl_0 * _ cself ){
_ Block_byref_val_0 * val = _ cself-> val;
(Val->__ forwarding-> val) = 1;
}A pointer is used to assign values to static variables. Assigning values to the _ block variable is more complex. The _ main_block_impl_0 struct instance of the Block holds a pointer to the _ Block_byref_val_0 struct instance of the _ block Variable. Hold a pointer to the instance itself in the idiom Variable _ forwarding of the _ Block_byref_val_0 struct instance. Access the member variable val through _ forwarding. (Here val is the variable held by the instance itself, which is equivalent to the original automatic variable)
About _ forwarding will be further discussed in the sequel.
----- 2014/3/18 Beijing