Goal:
- Familiar with Block basic principles
- Familiar with memory copy
How do you learn the concepts in depth? Convert the OC code to C++ code to see its internal implementation. During the interview process, each question has a corresponding score, be sure to answer as well as possible.
What is a Block
Often meet try:
- What is the Block?
- What do you make of the call to Block?
- Intercepting variables is a feature of blocks. How does the system intercept blocks?
- What do we do with the __block modifier?
- Do stack blocks and heap blocks understand when a Block needs to be copied?
6. Circular references to blocks
1. What is Block?
A Block is an object that encapsulates the execution function and context.
The source code parsing
+ (void)testBlock1{
int multiplier = 6;
int (^Block)(int) = ^int(int num) {
return num *multiplier;
};
Block(2);
}
Copy the code
Use clang-rewrite-objc file.m to view the contents of the compiled file
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __TestObject__testBlock1_block_impl_0 { struct __block_impl impl; struct __TestObject__testBlock1_block_desc_0* Desc; int multiplier; __TestObject__testBlock1_block_impl_0(void *fp, struct __TestObject__testBlock1_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static int __TestObject__testBlock1_block_func_0(struct __TestObject__testBlock1_block_impl_0 *__cself, int num) { int multiplier = __cself->multiplier; // bound by copy return num *multiplier; } static struct __TestObject__testBlock1_block_desc_0 { size_t reserved; size_t Block_size; } __TestObject__testBlock1_block_desc_0_DATA = { 0, sizeof(struct __TestObject__testBlock1_block_impl_0)}; // _C stands for class method, _I instance method, TstObject stands for class name, Static void _C_TestObject_testBlock1(Class self, SEL _cmd) {int multiplier = 6; int (*Block)(int) = ((int (*)(int))&__TestObject__testBlock1_block_impl_0((void *)__TestObject__testBlock1_block_func_0, &__TestObject__testBlock1_block_desc_0_DATA, multiplier)); ((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2); }Copy the code
A Block call is a function call. Fetching a function pointer from a Block passes two arguments, Block, and the required arguments.
Intercept variable
- Intercepts values for local variables of primitive data types.
- Local variables of object types are intercepted (strong references) along with ownership modifiers.
- Intercepts local static variables in pointer form.
- Do not intercept global variables, static global variables.
use
- (void)method {
// Local variable of primitive type
int var =1;
// A local variable of the object type
__unsafe_unretained id unsafe_obj = nil;
__strong id strong_obj = nil;
Static local variables
static int static_var = 3;
void(^Block)(void) = ^{
NSLog(@"Local variable < primitive data type > var %d".var);
NSLog(@"Local variable <__unsafe_unretained object type > var %@",unsafe_obj);
NSLog(@Local variable <__strong object type > var %@,strong_obj);
NSLog(@"Static variable %d",static_var);
NSLog(@"Global variable < primitive data type > var %d",global_var);
NSLog(@"Static global variable < primitive data type > var %d",static_global_var);
// Using the object type will prevent circular references to prompt
// NSLog(@"TestObject object type member num %d",self.num);
};
Block();
}
Copy the code
clang -rewrite-objc -fobjc-arc file.m
struct __TestObject__method_block_impl_0 {
struct __block_impl impl;
struct __TestObject__method_block_desc_0* Desc;
int var;
__unsafe_unretained id unsafe_obj;
__strong id strong_obj;
int *static_var;
__TestObject__method_block_impl_0(void *fp, struct __TestObject__method_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_var, int flags=0) : var(_var), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_var(_static_var){ impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __TestObject__method_block_func_0(struct __TestObject__method_block_impl_0 *__cself) {
int var = __cself->var; // bound by copy
__unsafe_unretained id unsafe_obj = __cself->unsafe_obj; // bound by copy
__strong id strong_obj = __cself->strong_obj; // bound by copy
int *static_var = __cself->static_var; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_0,var);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_1,unsafe_obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_2,strong_obj);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_3,(*static_var));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_4,global_var);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_89_hqtxy5r12y19qtg10mhvjrgr0000gn_T_TestObject_86265e_mi_5,static_global_var);
}
Copy the code
__block qualifier
Usage Scenario: In general, the __bloc modifier is required to perform an assignment to an intercepted variable.
Variables decorated with __block become objects.
+ (void)testBlock1{
__block int multiplier = 6;
int (^Block)(int) = ^int(int num) {
return num *multiplier;
};
Block(2);
}
// compiled into a C++ file
struct __Block_byref_multiplier_0 {
void *__isa;
__Block_byref_multiplier_0 *__forwarding;
int __flags;
int __size;
int multiplier;
};
struct __TestObject__testBlock1_block_impl_0 {
struct __block_impl impl;
struct __TestObject__testBlock1_block_desc_0* Desc;
__Block_byref_multiplier_0 *multiplier; // by ref
__TestObject__testBlock1_block_impl_0(void *fp, struct __TestObject__testBlock1_block_desc_0 *desc, __Block_byref_multiplier_0 *_multiplier, int flags=0) : multiplier(_multiplier->__forwarding){ impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void _C_TestObject_testBlock1(Class self, SEL _cmd) {
__attribute__((__blocks__(byref))) __Block_byref_multiplier_0 multiplier = {(void*)0,(__Block_byref_multiplier_0 *)&multiplier, 0, sizeof(__Block_byref_multiplier_0), 6};
int (*Block)(int) = ((int (*)(int))&__TestObject__testBlock1_block_impl_0((void *)__TestObject__testBlock1_block_func_0, &__TestObject__testBlock1_block_desc_0_DATA, (__Block_byref_multiplier_0 *)&multiplier, 570425344));
((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2);
}
Copy the code
Use does not equal assignment
Pen test
// Question: Does array need to be decorated with __block
NSMutableArray * array = [[NSMutableArray alloc] init];
void (^Block)(void) = ^{
[array addObject:@123];
};
Block();
NSLog(@"array = %@",array);
Copy the code
Not needed here, just for use. Whether the object type is a primitive data type or an object type, assignment requires the addition of a __block modifier.
When I assign to a variable,A __block modifier is required
Block memory Management
Block Copy operation
The same __block variable can be accessed from any memory location.
A circular reference
_array = [NSMutableArray arrayWithObject:@"block"]; _strBlk = ^NSString *(NSString *num) { return [NSString stringWithFormat:@"helloc_%@",_array[0]]; }; _strBlk(@"hello"); _array = [NSMutableArray arrayWithObject:@"block"]; __weak NSArray * weakArray = _array; _strBlk = ^NSString *(NSString *num) { return [NSString stringWithFormat:@"helloc_%@",weakArray[0]]; }; _strBlk(@"hello");Copy the code
Under MRC, circular references are not generated. Under ARC, circular references are generated, causing memory leaks.
If not called or not called for a long time, the ring will always exist.