1. Block profile
Chapter 2 Block implementation will cover chapter 1 in detail!!
1.1 What is a Block
Blocks are anonymous functions with automatic variables (local variables). It is an extension of the C language (which does not allow such anonymous functions). Is an object that encapsulates a function and its context.
int func(int a);
// When called:
int result = func(20);/ / func function name
// You can also call a function through a pointer
// When assigning to a function pointer, the address of the function must be obtained by the function name.
int (*funcP)(int) = func;
int result = (*funcP)(10);
// With blocks, you can use anonymous functions, that is, functions without function names. Such as:
int(^blk)(int) = ^ (int a){returna; };int result = blk(10);
Copy the code
1.2 Block syntax
Block declaration and definition
Standard declaration and definition
return_type (^blockName)(parameter_type) = ^return_type (parameter_type parameterName) {
// ...
};
/ / short
void (^blockName)(parameter_type) = ^(parameter_type parameterName) {
// ...
};
blockName(parameter);
// The parameter type is void
return_type (^blockName)(void) = ^return_type (void) {
// ...
};
/ / short
return_type (^blockName)(void) = ^ {// ...
};
blockName();
Copy the code
Anonymous Block: When a Block is implemented, the right side of the equals sign is an anonymous Block, which has no blockName and is called an anonymous Block
^return_type (parameter_type parameterName) {
// ...
};
Copy the code
Inline use of Block
// The anonymous Block is called immediately after the declaration
^return_type (parameter_type parameterName) {
// ...
}(paramete);
//Block calls itself internally, and recursive calls are the basis for many algorithms, especially if loop termination conditions cannot be predicted in advance.
// Note: Since blocks refer to themselves internally, __block must be used here to avoid the circular reference problem.
__block return_type (^blockName)(parameter_type) = [^return_type ((parameter_type parameterName){
if (returnCondition) {
blockName = nil;
return;
}
// ...
// recursive call
blockName(parameterName);
} copy];
// First call
blockName(parameter);
Copy the code
Typedefs simplify the declaration of blocks
/ / declare
typedef NSInteger(^ClickBlock_t)(NSInteger);
/ / block as attributes
@property (nonatomic.copy) ClickBlock_t clickBlock;
@property (nonatomic.copy) NSInteger(^ClickBlock)(NSInteger);
/ / block as a parameter
- (NSInteger)methodUsingBlock:(ClickBlock_t)clickBlock;
- (NSInteger)methodUsingBlock:(NSInteger(^) (NSInteger val))clickBlock;
Copy the code
1.3 Intercepted values of automatic variables
Local variable intercept value for the base data type
int main(int argc, const char * argv[]) {
int val = 10;
char *fmt = "val = %i\n";
typedef void(^blk_t)(void); blk_t blk = ^{printf(fmt, val); }; val =2;
fmt = "After change: val = % I \n";
blk();// Output: val = 10
return 0;
}
Copy the code
In this source code, the Block expression (the expression to the right of =) is assigned to the blK_T variable BLK. Instead of printing the overwritten value changed: val = 2, the output is: val = 10.
Because a Block expression holds the value of an automatic variable (that is, the instant value of that automatic variable), even rewriting the value of an automatic variable used in a Block after execution of the Block syntax does not affect the value of the automatic variable when the Block is executed.
This is the automatic interception of variable values.
Value capture of automatic variables can only hold instantaneous values that execute Block syntax. Once saved, it cannot be modified. Note: Modification here refers to assignment, whereas object types such as NSMutableArray can be modified using its methods, but cannot be assigned.
int main(int argc, const char * argv[]) {
NSMutableArray *arrM = [NSMutableArray array];
void(^blk)(void) = ^{[arrM addObject:[NSObjectnew]]; }; blk();return 0;
}
Copy the code
Note: Blocks cannot use automatic variables of C array types directly; they must use Pointers to C arrays. Examples are as follows:
const char text[] = "hello";
void(^blk)(void) = ^{ printf("%c\n", text[2]); };
Copy the code
It just uses C’s array of string literals, without assigning values to the captured automatic variables, so it looks fine. The following compilation error is actually generated.
This is because in Blocks, the method of intercepting automatic variables does not intercept C arrays. In this case, using Pointers can solve the problem.
const char *text = "hello";
void(^blk)(void) = ^{ printf("%c\n", text[2]); };
Copy the code
1.4 __block specifier
Value capture of automatic variables can only hold instantaneous values that execute Block syntax. Once saved, it cannot be modified. Try to change it:
int main(int argc, const char * argv[]) {
int val = 0;
void(^blk)(void) = ^{val = 1; }; blk();return 0;
}
Copy the code
This source code generates a compilation error: Variable not assignable (missing __block specifier)
error: variable is not assignable (missing __block type specifier)
If you want to assign a value in a Block expression to an automatic variable declared outside the Block syntax, you need to append a __block specifier to that self. In the source code, if we append a __block specifier to the automatic variable declaration int val, we now assign a value within the Block.
int main(int argc, const char * argv[]) {
__block int val = 10;
char *fmt = "val = %i\n";
void(^blk)(void) = ^{val = 1; }; blk(); printf(fmt, val);// Output: val = 1
val = 3;
blk();
printf(fmt, val);// Output: val = 3
return 0;
}
Copy the code
2. Block implementation
2.1 Implementation of Block (Essence)
The simplest Block syntax, omitting the return value and argument list.
int main(int argc, const char * argv[]) {
void(^blk00)(void) = ^{printf("blk00\n"); }; blk00();void(^blk01)(void) = ^{printf("blk01\n"); }; blk01();return 0;
}
Copy the code
Convert OC source to C++ source with the terminal command: clang-rewrite-objc source file:
// The basic block structure of the system declaration, which stores the basic information of the block.
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
Printf ("blk00\n"); ^{printf("blk00\n"); }) generated Block structure.
struct __main_block_impl_0 {
// Member variables
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// constructor
__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; }};// A function generated from the 0th occurrence of Block syntax in main
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("blk00\n");
}
// The 0th occurrence of the Block syntax in the main function generates a Block describing 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)};
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
__main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_1(struct __main_block_impl_1 *__cself) {
printf("blk01\n");
}
static struct __main_block_desc_1 {
size_t reserved;
size_t Block_size;
} __main_block_desc_1_DATA = { 0.sizeof(struct __main_block_impl_1)};
/ / the main function
int main(int argc, const char * argv[]) {
/ / OC source: void (^ blk00) (void) = ^ {printf (" blk00 \ n "); };
void(*blk00)(void) = ((void (*)()) &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)
);
//OC source code: blk01();
((void (*)(__block_impl *))((__block_impl *)blk00)->FuncPtr)((__block_impl *)blk00);
void(*blk01)(void) = ((void (*)())&__main_block_impl_1((void *)__main_block_func_1, &__main_block_desc_1_DATA)
);
((void (*)(__block_impl *))((__block_impl *)blk01)->FuncPtr)((__block_impl *)blk01);
return 0;
}
Copy the code
2.1.1 __main_block_func_0
The OC source code for Block 0 (BLK00) is divided into several sections for a step-by-step understanding.
First, the 0th Block syntax expression (anonymous function) in the OC source code:
^{printf("blk00\n"); };Copy the code
Convert to C++ source to get the function:
// __cself is equivalent to self pointing to the object itself in the OC instance method
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("blk00\n");
}
Copy the code
As the transformed source code shows, the anonymous functions used by Blocks are actually treated as simple C functions. Function name, named after the name of the function to which the Block syntax belongs (main) and the sequential value (0) in which the Block syntax appears in the function.
The function’s argument __cself is equivalent to the OC instance method’s variable self pointing to the object itself, that is, the argument __cself is a variable pointing to the Block value. The function __main_block_func_0 of this Block transformation does not use __cself.
Struct __main_block_IMPL_0 *__cself The argument is a pointer to the __main_block_IMPL_0 structure (implementation of the 0th Block occurring in main).
The __main_block_IMPL_0 structure (omitting the constructor) is declared as follows:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
};
Copy the code
2.1.2 __block_impl
The first member variable of this structure is the most basic block structure declared by the system, which is used to store the basic information of the block. Its structure is declared as follows:
// The basic block structure of the system declaration, which stores the basic information of the block.
struct __block_impl {
void *isa; // The block has an ISA pointer inside it, so it is essentially an OC object.
int Flags; / / logo
int Reserved; // Areas required for future version upgrades
void *FuncPtr; // Function pointer};Copy the code
The second member variable is the description structure pointer Desc for the 0th Block to appear in main. The __main_block_desc_0 structure is declared as follows:
// The description structure of the 0th occurrence Block in main
static struct __main_block_desc_0 {
size_t reserved; // Areas required for future version upgrades (more on that later)
size_t Block_size; // The size of the block
};
Copy the code
Initialize the __main_block_IMPL_0 constructor:
__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 first argument to the constructor is a function pointer; The second argument is the static global variable __main_block_desc_0 struct instance pointer; The third argument is int (when using this function to construct a structure, the third argument can be omitted; the default is 0).
The _NSConcreteStackBlock function that initializes the __block_impl isa member is called:
/ / OC source: void (^ blk00) (void) = ^ {printf (" blk00 \ n "); }; The conversion is as follows:
void(*blk00)(void) = ((void (*)()) &__main_block_impl_0( (void *) __main_block_func_0, &__main_block_desc_0_DATA )
);
Copy the code
Unreadable, break it up:
// Get the function pointer
void *p_func_0 = (void *)__main_block_func_0;
// Get the __main_block_desc_0 struct instance pointer
struct __main_block_desc_0 *p_desc_0 = &__main_block_desc_0_DATA;
// Get the __main_block_impl_0 structure instance pointer
struct __main_block_impl_0 *p_blk_impl_0 = &__main_block_impl_0(p_func_0, p_desc_0);
void(*blk00)(void);
blk00 = (void (*)())p_blk_impl_0;
/ / the above two lines of code equivalent: void blk00 (*) (void) = p_blk_impl_0 (void (*) ());
Copy the code
OC Block:
//void(^blk00)(void) = ^{printf("blk00\n"); }; equivalent
void(^blk00)(void);
blk00 = ^{printf("blk00\n"); };// blk00 = (void (*)())p_blk_impl_0;
Copy the code
^{printf(“blk00\n”);) Assign to the variable blk00 of type Block. The __main_block_impl_0 structure instance pointer is assigned to the Block blk00 variable, so the Block (^{printf(“blk00\n”);) in the source code Is an automatic variable of the __main_block_IMPL_0 structure type, which is an instance of the __main_block_IMPL_0 structure generated on the stack memory. Initialize the second member variable as follows:
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
As you can see, the source code is initialized with the size of the __main_block_IMPL_0 structure instance. Let’s look at how the __main_block_IMPL_0 struct instance on the stack (that is, Block) is initialized with these parameters.
Expand the __block_impl structure of the __main_block_IMPL_0 structure, which can be described as follows:
struct __main_block_impl_0 {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0* Desc;
};
Copy the code
The constructor does the following initialization based on the structure:
isa = &_NSConcreteStackBlock;
Flags = 0;
Reserved = 0
FuncPtr = __main_block_func_0;
Desc = __main_block_desc_0_DATA;
Copy the code
To call the Block section, the transformation looks like this:
//OC source code: blk00(); Translation:
((void (*)(struct __block_impl *))((struct __block_impl *)blk00)->FuncPtr)((struct __block_impl *)blk00);
Copy the code
Break it down:
// Block structure instance pointer
struct __block_impl *p_blk00 = (struct __block_impl *)blk00;
// Function pointer
void (*p_func)(struct __block_impl *) = (void (*)(struct __block_impl *))(p_blk00->FuncPtrl);
p_func(p_blk00);
Copy the code
It is very clear that it is simply calling a function using a function pointer. As confirmed, the pointer to the __main_block_func_0 function derived from Block syntax is assigned to the member variable FuncPtr. It is also proved that the struct __main_block_impl_0 *__cself parameter of function __main_block_func_0 points to a Block value. Block is passed as an argument.
2.1.3 Block is an OC object
Now, _NSConcreteStackBlock. To understand it, first understand the nature of OC classes and objects. In fact, a Block is an OC object.
The objc_object structure is used to store OC objects. The structure is declared as follows:
struct objc_object {
Class isa;
};
Copy the code
Class is a pointer to the objc_class structure, which is declared as follows:
typedef struct objc_class *Class;
struct objc_class {
Class isa;
// ...
};
Copy the code
Objc_object and objC_class are the most basic constructs used in the implementation of every concrete object and class. Verify this by using the OC class:
// BLKObject.h
@interface BLKObject : NSObject {
int val01;
int val02;
}
@end
Copy the code
Convert to C++ source code:
// The most basic structure used by object and class implementations
struct NSObject_IMPL {
Class isa;
};
//BLKObject A structure used in object and class implementations
struct BLKObject_IMPL {
struct NSObject_IMPL NSObject_IVARS;// The most basic structure used by object and class implementations
int val01;
int val02;
};
Copy the code
Compare the structure of blocks:
// Block implements the most basic structure.
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void*FuncPtr; };// The structure used in the 0th occurrence of the block implementation in main
struct __main_block_impl_0 {
struct __block_impl impl;// The most basic structure used by the block implementation
struct __main_block_desc_0* Desc;
};
Copy the code
Struct objc_class * unset NSObject_IMPL; unset NSObject_IMPL; unset NSObject_IMPL;
struct BLKObject_IMPL {
struct objc_class *isa;
int val01;
int val02;
};
Copy the code
So back to the Block structure:
struct __main_block_impl_0 {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
struct __main_block_desc_0* Desc;
};
Copy the code
This __main_block_IMPL_0 structure is equivalent to the BLKObject_IMPL (OC object) structure based on the OBJC_Object structure. Initialize the member variable ISA to _NSConcreteStackBlock, that is, _NSConcreteStackBlock corresponds to the bjC_class instance. When a Block is handled as an object of an OC, information about the class is placed in _NSConcreteStackBlock.
2.2 Value realization of automatic variable interception
int main(int argc, const char * argv[]) {
int abc = 20;
int val = 10;
const char *fmt = "val = %i\n";
void(^blk)(void) = ^{printf(fmt, val); }; val =2;
fmt = "After change: val = % I \n";
blk();// Output: val = 10
return 0;
}
Copy the code
Convert to C++ source code:
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;
// A variable appended to a Block member is of exactly the same type as an automatic variable used by a Block expression,
// The values of automatic variables used by Block expressions are assigned to members of the Block's struct instance.
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; // bound by copy
int val = __cself->val; // bound by copy
printf(fmt, val);
}
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[]) {
int abc = 20;
int val = 10;
const char *fmt = "val = %i\n";
void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val)
);
val = 2;
fmt = "After change: val = % I \n";
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
Copy the code
Block syntax expression (^{printf(FMT, val); };) The automatic variable used in the __main_block_IMPL_0 structure is appended as a member variable:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
// A variable appended to a Block member is of exactly the same type as an automatic variable used by a Block expression,
// The values of automatic variables used by Block expressions are assigned to members of the Block's struct instance.
const char *fmt;
int val;
};
Copy the code
Automatic variables appended to the __main_block_IMPL_0 structure are of exactly the same type. Unused automatic variables in Block syntax expressions are not appended. The interception of automatic Block variables is only for automatic variables used in blocks (the variable ABC is not intercepted).
When initializing a struct instance, member variables appended by automatic variables are initialized based on the parameters passed to the constructor. The constructor call validates its arguments below.
void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val)
);
Copy the code
Break it down:
// Get the function pointer
void *p_func_0 = (void *)__main_block_func_0;
// Get the __main_block_desc_0 struct instance pointer
struct __main_block_desc_0 *p_desc_0 = &__main_block_desc_0_DATA;
// Get the __main_block_impl_0 structure instance pointer
struct __main_block_impl_0 *p_blk_impl_0 = & __main_block_impl_0(p_func_0, p_desc_0, fmt, val);
void(*blk00)(void) = (void (*)()) p_blk_impl_0;
Copy the code
The __main_block_IMPL_0 structure instance is initialized using the automatic variables FMT and val when Block syntax is executed. That is, in the source code, the __main_block_IMPL_0 structure instance is initialized as follows:
impl.isa = &_NSConcreteStackBlock;
impl.Flags = 0;
impl.FuncPtr = &(__main_block_func_0);
Desc = &(__main_block_desc_0_DATA);
fmt = "val = %i\n";
val = 10;
Copy the code
Thus, automatic variable values are intercepted in the __main_block_IMPL_0 struct instance (that is, Block). Let’s look again at the implementation of anonymous functions using blocks. The Block syntax for the original source code looks like this:
^{printf(fmt, val); }Copy the code
The source code yields the following functions:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
const char *fmt = __cself->fmt;
int val = __cself->val;
printf(fmt, val);
}
Copy the code
In C++ source code, automatic variables used by Block expressions are captured on members of an instance of the __main_block_impl_0 structure. These variables are declared and defined before the Block expression. Thus, the original OC source code expression can be executed without modification using captured automatic variable values. When a Block expression is executed, the value of the automatic variable used by the Block expression is assigned to a member of the struct instance of the Block.
Why blocks can’t directly use C array type automatic variables. As mentioned earlier, when an automatic variable is intercepted, the value is passed to the constructor of the structure for assignment. C arrays cannot be assigned by array variables.
//error: array initializer must be an initializer list or string literal
// Array initialization rules:
// 1. Use "{}" or "string" when declaring.
Char text[4] = {'a', 'b', 'c', \0}; Char text[] = "ABC ";
// 2. Initialize one by one after the declaration
// Text [0] = 'a';
/ /...
Copy the code
2.3 Implementation of __block specifiers
A captured automatic variable used in a Block. Only the value of the automatic variable is captured. Once an automatic variable is used in a Block, overriding it in a struct instance of the Block does not change the previously intercepted automatic variable. The following source code attempts to change the automatic variable val in a Block.
int val = 0;
void(^blk)(void) = ^{ val = l; };Copy the code
The source code generates the following compilation errors:
error: variable is not assignable (missing __block type specifier)
// Variables are not assignable (missing __block type specifier)
void (^blk)(void)= ^ { val = 1; }; ~ ~ ~ ^Copy the code
As mentioned earlier, because it is implementationally impossible to overwrite the value of an intercepted autovariable, a compilation error occurs when the compiler detects an assignment to an intercepted autovariable during compilation. But then you can’t save the value in the Block, which is very inconvenient.
2.3.1 C language variable, Block change value
There are two ways to solve this problem. First: C has variables that allow blocks to overwrite values. Details are as follows:
Static variables. Static global variables. 3. Global variables
While the anonymous function part of Block syntax is simply transformed into C functions, accessing static global variables from the function of this transformation is unchanged and can be used directly. Take a look at the following source code:
// Global variables
int global_val = 1;
// Static global variables
static int static_global_val = 2;
int main(int argc, const char * argv[]) {
// Static variables
static int static_val = 3;
void(^blk)(void) = ^{
global_val += 1;
static_global_val += 1;
static_val += 1;
};
blk();
return 0;
}
Copy the code
The source code uses Block to override static variables static_val, static global variables static_global_val, and global variables global_val. The C++ source code is as follows:
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;
// Only static_val is appended as a pointer to a Block member variable
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) {
// Use a pointer appended to the Block member variable static_val
int *static_val = __cself->static_val;
(*static_val) += 1;
// Take the value directly
global_val += 1;
static_global_val += 1;
}
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[]) {
static int static_val = 3;
void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
return 0;
}
Copy the code
Access to the global variables global_val and static_global_val is the same as before the conversion. Global and static variables are not intercepted and are directly evaluated, while static_val is intercepted as a pointer.
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *static_val;
}
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int *static_val = __cself->static_val;
global_val += 1;
static_global_val += 1;
(*static_val) += 1;
}
Copy the code
Access it using a pointer to the static variable static_val. Pass a pointer to the static variable static_val to the __main__block_IMPL_0 constructor and save it. This is the easiest way to use variables out of scope.
This approach to static variables seems to work for automatic variable access as well. However, when the scope of a variable ends, the original automatic variable is discarded, so a variable that exists outside the scope of a variable in a Block will not be able to access the original automatic variable through a pointer, just like a static variable. In fact, automatic variables of intercepted objects beyond their variable scope can exist in blocks generated by Block expressions. These are described in the next section.
A second way to solve the problem of not holding values in blocks is to use the __block specifier. A more accurate expression is the __block storage domain class specifier (__block storage-class-specifier). C has the following storage domain class specifiers:
typedef / extern / static / auto / register
2.3.2 Using the __block specifier
The __block specifiers are similar to static, auto, and Register specifiers and are used to specify which storage field to store variable values in. For example, auto means stored in the stack as an automatic variable, and static means stored in the data area as a static variable. Let’s actually use the __block specifier, which specifies the automatic variable in the Block that we want to change the value of. Append a __block specifier to an automatic variable declaration:
int main(int argc, const char * argv[]) {
__block int val = 10;
void(^blk00)(void) = ^{ val = 1; };
void(^blk01)(void) = ^{ val = 2; };
return 0;
}
Copy the code
The converted source code looks like this:
// the __block variable structure
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};
/ / Block structure
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_val_0 *val; // by ref
__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; // bound by ref
(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((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t 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
};
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
__Block_byref_val_0 *val; // by ref
__main_block_impl_1(void *fp, struct __main_block_desc_1 *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_1(struct __main_block_impl_1 *__cself) {
__Block_byref_val_0 *val = __cself->val; // bound by ref
(val->__forwarding->val) = 2;
}
static void __main_block_copy_1(struct __main_block_impl_1*dst, struct __main_block_impl_1*src) {
_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_1(struct __main_block_impl_1*src) {
_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static struct __main_block_desc_1 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_1*, struct __main_block_impl_1*);
void (*dispose)(struct __main_block_impl_1*);
} __main_block_desc_1_DATA = {
0.sizeof(struct __main_block_impl_1), __main_block_copy_1, __main_block_dispose_1
};
int main(int argc, const char * argv[]) {
__Block_byref_val_0 val = {
(void*)0,
(__Block_byref_val_0 *)&val,
0.sizeof(__Block_byref_val_0),
10
};
void(*blk00)(void) = ((void (*)())&__main_block_impl_0(
(void *)__main_block_func_0,
&__main_block_desc_0_DATA,
(__Block_byref_val_0 *)&val,
570425344));void(*blk01)(void) = ((void (*)())&__main_block_impl_1(
(void *)__main_block_func_1,
&__main_block_desc_1_DATA,
( __Block_byref_val_0 *)&val,
570425344));return 0;
}
Copy the code
Simply append the __block specifier to the automatic variable, and the source code increases dramatically.
__block int val = 10;
Copy the code
To:
__Block_byref_val_0 val = {
(void*)0,
(__Block_byref_val_0 *)&val,
0.sizeof(__Block_byref_val_0),
10
};
Copy the code
The automatic variable val with the __block specifier is converted to the __Block_byref_val_0 struct type automatic variable, that is, the __Block_byref_val_0 struct instance on the stack.
The __Block_byref_val_0 structure is declared as follows:
struct __Block_byref_val_0 {
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val;
};
Copy the code
Obviously, the last member variable of this structure is the corresponding automatic variable val in the source code; This structure is the 0th block expression ^{val = 1 of an object (with an ISA pointer); } convert the following function:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_val_0 *val = __cself->val; // bound by ref
(val->__forwarding->val) = 1;
}
Copy the code
We just assigned a value to a static variable in a Block using a pointer to that static variable. Assigning to a __block variable is more complicated than this.
The __main_block_IMPL_0 struct instance (Block) holds a pointer to the __Block_byref_val_0 struct instance (__block variable). An instance of the __Block_byref_val_0 structure is an object, and again, Copy and dispose, the member variables added to the __main_block_DESc_0 structure, are used, along with the __main_block_copy_0 and __main_block_dispose_0 functions assigned to the member variable as Pointers.
The __forwarding member of the __Block_byref_val_0 structure instance holds a pointer to the instance itself. Access the member variable val. (val is a variable held by the instance itself and is equivalent to the original automatic variable) via the member variable __forwarding, as shown below:
The member variable __forwarding is explained later.
The __Block_byref_val_0 structure member of the __block variable val is not a member of the __main_block_IMPL_0 structure, so that the __block variable can be used in multiple blocks. As follows:
__block int val = 10;
void(^blk00)(void) = ^{ val = 1; };
void(^blk01)(void) = ^{ val = 2; };
Copy the code
Block variables all access the __block variable val, which is converted to c++ source code excerpt (simplified) :
__Block_byref_val_0 val = {(void*)0, &val, 0.sizeof(__Block_byref_val_0), 10};
blk00 = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &val, 570425344);
blk01 = &__main_block_impl_1(__main_block_func_1, &__main_block_desc_1_DATA, &val, 570425344);
Copy the code
Both blocks use Pointers to the __Block_byref_val_0 structure instance val. That is, the same __block variable can be used from multiple blocks. Also use multiple __block variables in a Block. At this point you should be able to understand the __block variable.
Copy and dispose added member variables to the __main_block_desc_0 structure are explained later in 2.6.
The reason a Block can exist outside the scope of a variable. Explained in 2.4.
The _ block variable uses the structure member variable __forwarding for a reason. Explained in 2.5.
2.4 Block Storage Domains
As you can see from the previous description, Block is automatically converted to Block’s struct type variable, and __block is automatically converted to __block’s struct type variable. Automatic variables of a struct type are instances of that struct generated on the stack.
In addition, as you can see from the previous description, blocks are also OC objects. When a Block is considered as an OC object, the Block’s class is _NSConcreteStackBlock. Although this class does not appear in the transformed source code, there are many similar classes:
__NSStackBlock__
Objects of this class are stored on the stack.__NSGlobalBlock__
Class objects are stored in the program’s data area (.data area).__NSMallocBlock__
Class objects are stored in chunks of memory (that is, the heap) allocated by the Malloc function.
Against 2.4.1 NSGlobalBlock
int val = 10;
typedef int(^blk_t)(int);
blk_t blk_0 = ^(int count){returncount; }; blk_t blk_1 = ^(int count){returnval + count; };int main(int argc, const char * argv[]) {
blk_t blk_2 = ^(int count){returncount; };NSLog(@ "% @", blk_0);/ / print __NSGlobalBlock__
NSLog(@ "% @", blk_1);/ / print __NSGlobalBlock__
NSLog(@ "% @", blk_2);/ / print __NSGlobalBlock__
return 0;
}
Copy the code
The classes of the above three blocks are all NSGlobalBlock classes.
Blk_0 and BLK_1 are stored in the data area of the program with Block struct instances. Because automatic variables cannot be used where global variables are used, there is no interception of automatic variables (BLK_1 accesses global variables directly). Blk_2 also did not capture automatic variables.
The contents of the above three Block instances do not depend on the state at execution, so only one instance is needed in the entire program. So just set the Block struct instance in the same data area as the global variable. Blocks can be stored in the data area of a program as long as automatic variables are not intercepted.
2.4.2 NSStackBlock
A Block is an NSStackBlock as long as an automatic variable is captured and not copied.
int main(int argc, const char * argv[]) {
typedef int(^blk_t)(int);
int val = 10;
blk_t blk_0 = ^(int count){returnval + count; };NSLog(@ "% @", blk_0);
// Print NSStackBlock in non-ARC, but NSMallocBlock in ARC
// In most cases in ARC, the compiler makes the appropriate judgment and automatically generates code that copies blocks from the stack to the heap,
// In non-ARC, manual copy is required.
return 0;
}
Copy the code
Blk_0 is NSStackBlock in non-ARC, but NSMallocBlock in ARC, because in ARC, the compiler makes the appropriate judgment and automatically copies blocks from the stack to the heap in most cases, whereas in non-ARC, Manual copy is required.
2.4.3 NSMallocBlock
Just copy nsStackBlocks to get the NSMallocBlock class blocks stored on the heap (in most cases in ARC the compiler makes the appropriate judgment and automatically copies blocks from the stack to the heap). Remaining issues from the previous section:
● The reason why a Block can exist outside the scope of a variable
● the reason for the existence of the __forwarding struct member variable
Blocks stored on global variables can also be safely used by Pointers from outside the variable scope. A Block on the stack, however, is discarded if the scope of the variable to which it belongs ends. Since a __block variable is also configured on the stack, it is also deprecated if the scope of the variable to which it belongs ends.
Blocks provide a way to solve this problem by copying blocks and __block variables from the stack to the heap. Copies blocks configured on the stack to the heap so that blocks on the heap continue to exist even after the variable scope described by the Block syntax ends. The __block variable structure member __forwarding provides proper access to __block variables whether they are configured on the stack or heap.
Sometimes __block variables stored on the heap can also be accessed on the stack. As long as the struct instance member variable __forwarding on the stack refers to the struct instance on the heap, it is properly accessible from either the stack or the heap __block variable.
In fact, when ARC is in effect, in most cases the compiler makes the appropriate judgment and automatically generates code that copies blocks from the stack to the heap. Let’s look at the following function that returns a Block.
typedef int(^blk_t)(int);
blk_t func(int val) {
return^ (int count){returnval + count; }; }Copy the code
The source code is a function that returns a Block configured on the stack. That is, the scope of the variable ends when the caller is returned from the function during program execution, so the Block on the stack is discarded. Despite this problem, the source code can be converted by the compiler corresponding to ARC as follows:
blk_t func(int val) {
blk_t tmp = &_func_block_impl_0(_func_block_func_0, &_func_block_desc_0_DATA, val);
tmp = _Block_copy(tmp);
return objc_autoreleaseReturnValue(tmp);
}
Copy the code
The Block generated by Block syntax, the instance of the Block structure stored on the stack, is assigned to the variable TMP of Block type. The _Block_copy function copies blocks on the stack to the heap. After copying, assign the address on the heap as a pointer to the variable TMP. Register the Block on the heap as an OC object in autoReleasepool, and return that object.
When a Block is returned as a function return value, the compiler automatically generates code that is copied to the heap. I mentioned earlier that “most of the time the compiler makes the appropriate judgment,” but other than that you need to manually generate code to copy blocks from the stack to the heap. At this point we use the “copy instance method”.
The compiler cannot judge when passing a Block to a method or function argument. But if you have properly copied the parameters passed in the method or function (calling the copy method), you do not need to call the method or function before. Manual copy. The following methods or functions do not need to be manually copied.
- Cocoa framework methods with usingBlock in their names
- Grand Central Dispatch的API
For example, USES the NSArray class instances of enumerateObjectsUsingBlock method and dispatch_asyne function, don’t have to manually copy. In contrast, manual copying is required when passing blocks on the initWithObjects instance method of the NSArray class. Let’s take a look at the source code.
typedef int(^blk_t)(void);
id getBlockArray( int val){
return [[NSArray alloc] initWithObjects:^{NSLog(@"blk0:%d", val); ^ {},NSLog(@"blk1:%d", val); },nil];
}
int main(int argc, const char * argv[]) {
id obj = getBlockArray();
blk_t blk = (blk_t)[obj objectAtIndex:0];
blk();
return 0;
}
Copy the code
The getBlockArray method generates two blocks on the stack and passes them to the initWithObjects instance method of the NSArray class. In the getBlockArray method caller, take the Block from the NSArray object and execute.
An exception occurred while executing the source code, forcing the application to terminate. This is because at the end of the getBlockArray function, the Block on the stack is discarded and becomes a wild pointer. Because the compiler cannot determine whether a copy is needed when passing a Block to a method or function argument. So let the programmer copy manually in this case. The source code works with modifications like the following.
id getBlockArray(int val){
return [[NSArray alloc] initWithObjects:
[^{NSLog(@"blk0:%d", val); }copy], [^ {NSLog(@"blk1:%d", val); }copy].nil];
}
Copy the code
Based on the storage domain where blocks are stored, the actions of the copy method are summarized as follows:
Block of the class | The storage domain of the copy source | Print effect |
---|---|---|
NSStackBlock | The stack | Copy from stack to heap |
NSGlobalBlock | The data area of the program | Do nothing |
NSMallocBlock | The heap | Reference count increment |
No matter where the Block is stored, copying with the copy method does not cause any problems. Call the copy method when in doubt.
However, you cannot explicitly release in ARC, so try not to use a retain operation on a Block. No matter where the Block is configured, copying with copy does not cause any problems. Call the copy method when in doubt.
2.5 __block variable storage domain
__block variables are also affected when blocks that use them are copied from the stack to the heap. Table 2-5 lists the summary.
Storage domain for __block variables | The effect of a Block being copied from stack to heap |
---|---|
The stack | Copied from the stack to the heap and held by the Block |
The heap | Be Block to retain |
If a __block variable is used in a Block, all __block variables used are copied from stack to heap when the Block is copied from stack to heap. At this point, the Block holds the __block variable. Even if the Block is copied to the heap, copying the Block has no effect on the __block variable used.
When using a __block variable in multiple blocks, it is also configured on the stack because all blocks are configured on the stack first. When any Block is copied from stack to heap, the __block variable is copied from stack to heap and held by the Block. When the remaining blocks are copied from the stack to the heap, the copied Block holds the __block variable and increases the reference count of the __block variable.
Reference counting memory management is exactly the same as OC. Blocks that use a __block variable hold that variable. If a Block is deprecated, the __block variable it holds is released.
Now that we understand the storage domain for __block variables, let’s go back to section 2.4 and see why we use __block variables with the structure member variable __forwarding. A __block variable can be accessed correctly whether it is stored on the stack or on the heap. As stated in this sentence, when a __block variable is copied from the stack to the heap. __block variables on the stack and on the heap can be accessed at the same time. Source code is as follows:
__block int val = 0;
void(^blk)(void) = [^{ val = val + 1; }copy];
val = 10;
NSLog(@"val = %i",val);// val = 10
blk();
NSLog(@"val = %i",val);// val = 11
Copy the code
Use the copy method to copy Block syntax using __block variables. Block and __block variables are both copied from the stack to the heap. This code uses the initialized __block variable in Block syntax expressions.
In the Block expression (^{val = val + 1; }, val is an instance of a __block variable structure copied to the heap, and is used as a block-independent variable (val = 10;). Val, a structure instance for copying a __block variable on the stack. When a __block variable is copied from the stack to the heap, the value of the member variable __forwarding is replaced by the address of the __block variable on the target heap. As shown in the figure.
With the __forwarding pointer, the same __block variable can be accessed smoothly whether it is used in or out of the Block syntax, whether the __block variable is stored on the stack or heap.
2.6 Intercepted Objects
int main(int argc, const char * argv[]) {
void(^blk)(id);
{
NSMutableArray *arrM = [NSMutableArray array];
blk = [^(id obj){
[arrM addObject:obj];
NSLog(@"arrM count = %lu", arrM.count);
} copy];// This Block automatically copies when ARC occurs
}
blk([NSObject new]);
blk([NSObject new]);
blk([NSObject new]);
return 0;
}
Copy the code
When the scope of the variable ends, the variable arrM is deprecated and its strong reference is invalid, so the object assigned to the NSMutableArray class of the variable arrM must be freed and deprecated. But the source code works fine, and the result is as follows:
arrM count = 1
arrM count = 2
arrM count = 3
Copy the code
This result means that the object assigned to the NSMutableArray class of the variable arrM exists outside its variable scope in the execution part of the last Block of the source code. The source code converted by the compiler is as follows (some of the code is changed back to OC code for easy reading and strong conversion is deleted):
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSMutableArray *arrM;
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj) {
NSMutableArray *arrM = __cself->arrM;
[arrM addObject:obj];
NSLog(@"arrM count = %lu", arrM.count);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->arrM, (void*)src->arrM, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->arrM, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t 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(int argc, const char * argv[]) {
void(*blk)(id);
{
NSMutableArray *arrM = [NSMutableArray array];
blk = [&__main_block_impl_0(
__main_block_func_0, &__main_block_desc_0_DATA, arrM, 570425344
) copy];
}
*blk->FuncPtr)(blk, [NSObject new]);
*blk->FuncPtr)(blk, [NSObject new]);
*blk->FuncPtr)(blk, [NSObject new]);
return 0;
}
Copy the code
Note the automatic variable arrM that is assigned to the NSMutableArray class object and intercepted. We can see that it is a member variable in the Block structure.
However, object variables cannot be used as members of C language constructs, and the presence of OC object variables causes compilation errors. Because the compiler does not know when to initialize and discard C constructs, it cannot manage memory well. Unless forced to void * (ARC causes a compilation error) or changed the memory management modifier (__strong by default) to __ unsafe_unretained the modifier’s variables are not part of the compiler’s memory management object.
However, Block structures can have __strong member variables. Because the OC runtime library is able to accurately timing when blocks are copied from the stack to the heap and blocks on the heap are deprecated, Block structures can be properly initialized and deprecated even if they contain variables with __strong or __weak modifiers.
To do this, copy and dispose, the member variables added to the __main_block_DESc_0 structure, are used, along with the __main_block_copy_0 and __main_block_dispose_0 functions assigned to the member variable as Pointers.
Because the Block structure of the source code contains the object type variable arrM with the __strong modifier attached, the objects assigned to the variable arrM need to be managed properly. So the __main_block_copy_0 function uses the __Block_object_assign function to assign an object type object to the Block structure’s member variable arrM and holds the object.
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->arrM, (void*)src->arrM, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
Copy the code
The _Block_object_assign function calls the function equivalent to the retain instance method that assigns the object to a structure member variable of the object type.
In addition, the __main_block_dispose_0 function uses the _Block_object_dispose function, which is the equivalent of the release instance method, to release the object assigned to the Block structure object type member variable.
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->arrM, 3/*BLOCK_FIELD_IS_OBJECT*); }Copy the code
Although this __main_block_copy_0 function (hereinafter referred to as copy function) and __main_block_dispose_0 function (hereinafter referred to as dispose Pointers are assigned to __main_block_desc_0 structure member variables copy and Dispose, which are called when a Block is copied from the stack to the heap and when the Block on the heap is discarded.
function | Call time |
---|---|
The copy function | Blocks on the stack are copied to the heap |
The dispose function | Blocks on the heap are discarded |
Blocks on the stack are copied to the heap timing:
● When a Block’s copy instance method is called
●Block returns as a function return value
● When assigning a Block to a class with the __strong modifier ID type or to a member variable of Block type
● When passing blocks in Cocoa framework methods or GCD apis that have usingBlocks in their method names
That is, although the source code shows that blocks on the stack are copied to the heap in these cases, it really boils down to blocks being copied from the stack to the heap when the _Block_copy function is called. Instead, call the dispose function when the Block copied to the heap is released and no one is holding it so that it is discarded. This is equivalent to the object’s Dealloc instance method. With this construction, objects intercepted in a Block can exist outside their variable scope by using automatic variables with a __strong modifier. Although this method of using copy and dispose is not explained in Section 2.3, it is actually used when using __block variables.
The difference between intercepting objects and using the _ block variable
object | BLOCK_ FIELD_IS_OBJECT(3) |
---|---|
__block variable | BLOCK_ FIELD_IS_ BYREF(8) |
The BLOCK_ FIELD_IS_OBJECT and BLOCK_FIELD_IS_BYREF parameters are used to distinguish the object type of copy function and Dispose function from __block variable.
However, like the copy function holding the intercepted object and the Dispose function releasing the intercepted object, the copy function holding the used __block variable and the Dispose function releasing the used __block variable.
Thus, objects assigned to automatic variables with the __strong modifier and __block variables copied to the heap used in blocks can exist outside the scope of their variables because they are held by blocks on the heap.
Only a call to _Block_copy can hold the value of the intercepted automatic variable of the object type with the __strong modifier. Without _Block_copy, even if the object is intercepted, it will be discarded as the scope of the variable ends.
Therefore, when using object type automatic variables in a Block, it is recommended to call the Block’s copy instance method, except in the following cases.
●Block returns as a function return value
● When a Block is assigned to an ID type or Block type member variable of a class with the __strong modifier
● When passing blocks to Cocoa framework methods or GCD apis that have usingBlocks in their method names
2.7 __block variables and objects
The __block specifier can specify any type of automatic variable. The following specifies the automatic variable used to assign the OC object type.
int main(int argc, const char * argv[]) {
__block NSObject *obj = [NSObject new];
void(^blk00)(id) = ^ (id obj){ NSLog(@ "% @", obj); };
blk00(obj);
return 0;
}
Copy the code
When ARC is in effect, id and object type variables must have an ownership modifier attached, which defaults to variables with the _strong modifier. This code is replaced as follows:
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, * (void((* *)char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void((* *)char*)src + 40), 131);
}
struct __Block_byref_obj_0 {
void *__isa;
__Block_byref_obj_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj;
};
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; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj) {
NSLog(@"% @", obj);
}
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[]) {
__Block_byref_obj_0 obj = {
(void*)0,
(__Block_byref_obj_0 *)&obj,
33554432.sizeof(__Block_byref_obj_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
[NSObject new]};void(*blk00)(id) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
blk00->FuncPtr(blk00, (obj.__forwarding->obj));
return 0;
}
Copy the code
Here comes the _Block_object_assign and _Block_object_dispose functions described in the previous section.
In the case of an automatic object type variable with a __strong modifier attached to the Block, the _Block_object_assign function holds the objects that the Block intercepts when the Block is copied from the stack to the heap. When a Block on the heap is discarded, use the _Block_object_dispose function to dispose the objects that the Block intercepts.
The same process occurs when a __block variable is an automatic variable of an object type with a __strong modifier attached.
Thus, even if an object is assigned a copy to the __block variable of the object type with the __strong modifier on the heap, the object will continue to be held as long as the __block variable persists on the heap. This is the same as for objects in blocks that use automatic variables assigned to object types with the __strong modifier attached.
In addition, the only automatic variables we used previously were id types or object types with the __strong modifier. What if you use the __weak modifier? The first is the case of using an object type variable with an __weak modifier in a Block.
void(^blk)(id);
{
NSMutableArray *tmp = [NSMutableArray array];
__weak NSMutableArray *arrM = tmp;
blk = [^(id obj){
[arrM addObject:obj];
NSLog(@"arrM count = %lu", arrM.count);
} copy];
}
blk([NSObject new]);//arrM count = 1
blk([NSObject new]);//arrM count = 2
blk([NSObject new]);//arrM count = 3
Copy the code
2.8 Block circular reference
If you use an automatic object type variable with a __strong modifier in a Block, the object is held by the Block while it is copied from the stack to the heap. This can easily lead to circular references. Let’s take a look at the following source code:
//ZYPObject.h
ypedef void(^blk_t)(void);
@interface ZYPObject : NSObject {
blk_t _blk01;
id _obj02;
}
@end
//ZYPObject.m
- (instancetype)init {
if (self = [super init]) {
_blk01 = ^{NSLog(@"self = %@".self); }; }return self;
}
- (void)dealloc {
NSLog(@"dealloc");
}
@end
//main.m
int main(int argc, const char * argv[]) {
ZYPObject *obj = [[ZYPObject alloc] init];
NSLog(@ "% @", obj);
return 0;
}
Copy the code
The Dealloc instance method of the ZYPObject class in the source code must not have been called.
The Block member variable _BLk01 of the ZYPObject class object holds a strong reference assigned to Block. The ZYPObject class object holds a Block. The Block syntax executed in the init instance method uses the object type variable self with the __strong modifier. And because the Block syntax assignment is in the member variable _blk01, the Block generated on the stack by Block syntax is then copied from the stack to the heap, holding the self used. Self holds Block, Block holds self. This is circular reference.
To avoid this circular reference, declare a variable with an __weak modifier attached and assign self to it.
- (instancetype)init {
if (self = [super init]) {
__weak typeof(self) tmp = self;
_blk01 = ^{NSLog(@"self = %@", tmp); }; }return self;
}
Copy the code
In this source code, there is no need to determine whether the value of TMP is nil, since the zypobject object that holds the Block, self, assigned to the variable TMP, must exist while the Block exists.
In ios4-oriented applications, you must use the __unsafe_unretained modifier instead of the __weak modifier. The __unsafe_unretained modifier can also be used in this source code without worrying about wild Pointers.
__unsafe_unretained typeof(self) tmp = self;
_blk01 = ^{NSLog(@"self = %@",tmp); };Copy the code
In addition, the following source code without self in Bloc K also intercepted self, causing a circular reference.
- (instancetype)init {
if (self = [super init]) {
_blk01 = ^{NSLog(@"self = %@", _obj02); }; }return self;
}
Copy the code
That _obj02 used in Block syntax actually intercepts self. To the compiler, _obj02 is simply a member variable of the object’s use structure.
_blk01 = ^{NSLog(@"self = %@".self->_obj02); };Copy the code
The source code is basically the same as before, declaring variables with an __weak modifier and assigning _obj02 to be used to avoid circular references. The __unsafe_unretained modifier is also safe to use in this source code for the same reason.
- (instancetype)init {
if (self = [super init]) {
__weak typeof(self) tmp = _obj02;
_blk01 = ^{NSLog(@"self = %@", tmp); }; }return self;
}
Copy the code
Alternatively, you can use a __block variable to avoid circular references.
- (instancetype)init {
if (self = [super init]) {
__block typeof(self) block_self = self;
_blk01 = ^{NSLog(@"self = %@",block_self); }; block_self =nil;
}
return self;
}
Copy the code
Let’s compare the use of the __block variable to avoid recurring references with the __weak and __unsafe_unretained modifier. The advantages of using a __block variable are as follows:
● The holding period of an object can be controlled by a __block variable
● Use the __unsafe_unretained modifier for environments where the __weak modifier cannot be used (don’t worry about dangling Pointers).
Whether nil or other objects are assigned to a __block variable can be determined dynamically at Block time.
The disadvantages of using a __block variable are as follows: blocks must be executed to avoid circular references
Circular references are unavoidable when there are paths that execute Block syntax but do not execute Block. If a Block causes a circular reference, use the __block variable, __weak modifier, or __unsafe_unretained modifier to avoid a circular reference depending on the purpose of the Block.
2.9 the copy/release
When ARC is invalid, you generally need to manually copy blocks from the stack to the heap. Also, since ARC is not valid, the copied Block must be freed. We use copy instance method to copy and release instance method to release.
Calling the retain instance method does nothing for blocks stored on the stack. Blocks can only be held via the RETAIN instance method if they are copied and stored on the heap. Therefore, it is recommended to use the copy instance method to hold blocks.
Since Blocks are extensions of C, Block syntax can also be used in C. Use the _Block_copy and _Block_release functions instead of the copy/release instance methods. Use and think about reference counting in the same way as the copy/ Release instance method in OC.
In addition, __block specifiers are used to avoid circular references in blocks when ARC is invalid. This is because when a Block is copied from the stack to the heap, if the Block uses an automatic variable of object type with an __block specifier, it is not retained; If a Block uses an automatic variable of an object type without a __block specifier, it is retained.