1. The underlying structure of blocks
- First give the conclusion:
The essence of a block is a structure, and the methods executed within the block are Pointers to member variables – anonymous functions within the structure
- Verify:
// main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block1)(void) = ^ {NSLog(@"i'm a NSGlobalBlock block");
};
block1();
NSLog(@ "% @", block1);
}
return 0;
}
Copy the code
Compile main.m to get c++ source code:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
Copy the code
Get the source main.cpp:
/// Block structure
struct __main_block_impl_0 {
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; }};/// the method executed inside the block is now a function
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6f_2snw6yxn2sz630d3r61gc0700000gn_T_main_a2088f_mi_0);
}
/// The implementation structure within the block
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
/// Block information
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 (*block1)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6f_2snw6yxn2sz630d3r61gc0700000gn_T_main_a2088f_mi_1, block1);
/// simplify
// create a block structure
block1 = __main_block_impl_0(__main_block_func_0, __main_block_desc_0_DATA);
/// call an anonymous function
block1->funcPtr(a); }return 0;
}
Copy the code
The first block contains the isa pointer and the anonymous function pointer, and the second block contains the basic information (size) of the block.
Therefore, we conclude that the block structure is basically as follows:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __block_info {
size_t reserved;
size_t Block_size;
};
typedef struct _SanjiBlock {
struct __block_impl impl;
struct __block_info info;
} *sanjiBlock;
Copy the code
We can override the above blocks with _SanjiBlock
// Call block directly
block1();
// call by getting a pointer to the function
sanjiBlock sanjiBlock = (__bridge void *)block1;
void (*p)(void) = sanjiBlock->impl.FuncPtr;
p();
// Print the same result:
2020- 12- 09 14:35:15.207888+0800 BlockDetail[39554:1284881] i'm a NSGlobalBlock block 2020-12-09 14:35:15.207928+0800 BlockDetail[39554:1284881] I 'm a NSGlobalBlock block
Copy the code
2. Capture (capture
), and __block
- Capture of local variables (
auto
.static
)
int main(int argc, const char * argv[]) {
@autoreleasepool {
/// auto can be omitted
auto int age1 = 10;
void (^block1)(void) = ^ {NSLog(@"age1 is %d", age1);
};
age1 += 10;
block1();
static int age2 = 10;
void (^block2)(void) = ^ {NSLog(@"age2 is %d", age2);
};
age2 += 10;
block2();
}
return 0;
}
/// Print the result
age1 is 10
age2 is 20
Copy the code
Still compile main.m:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age1;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age1, int flags=0) : age1(_age1) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
int *age2;
__main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int *_age2, int flags=0) : age2(_age2) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
Block1 captures the value of age1, and block2 captures the address of age2 for a simple reason: Age1 is an auto local variable that is destroyed when it leaves scope, so block1 can only capture its value (before it is destroyed), whereas static age2 is an in-memory variable, and Block2 captures its address and always has access to the latest value.
Block
Will global variables be captured?
No, because the global variable is always in memory and can be accessed directly to get the latest value.
- Therefore, the following conclusions can be drawn:
Variable types | Whether it will be caught by a block | access |
---|---|---|
Auto local variable | will | Value passed |
Static local variable | will | Pointer passed |
The global variable | Don’t | Direct access to the |
3. Type of block
Block type | The environment |
---|---|
NSGlobalBlock | The auto variable is not accessed |
NSStackBlock | The auto variable is accessed |
NSMallocBlock | NSStackBlock call copy |
Code :(in ARC environment)
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block1)(void) = ^ {NSLog(@"i'm block1");
};
int age = 10;
void (^block2)(void) = ^ {NSLog(@"age is %d", age);
};
void (^block3)(void) = [block2 copy];
NSLog(@"\nblock1 is %@\nblock2 is %@\nblock3 is %@\n", [block1 class], [block2 class], [block3 class]);
}
return 0;
}
/// Print the result
block1 is __NSGlobalBlock__
block2 is __NSMallocBlock__
block3 is __NSMallocBlock__
Copy the code
ARC copies the block from the stack to the heap, and references the counter +1. If the block is on the stack, it will be released when it is out of scope. Calling blocks at this point produces unpredictable results, leaving it up to the programmer to manage memory in the heap.
Change the environment to MRC:
block1 is __NSGlobalBlock__
block2 is __NSStackBlock__
block3 is __NSMallocBlock__
Copy the code
In what other cases are blocks automatically copied to the heap?
block
When the method return value is returned- will
block
Assigned to__strong
Pointer time block
It’s available as cocoa APIusingBlock
When the parameters of the
4, __Block
__Block
Used to modifyblock
It cannot be modified internallyauto
variable__Block
The decorated variable is wrapped as an object
4. Block memory management
Let’s start with two pieces of code: ARC
@interface OnePerson(a)
@property(nonatomic.assign) int age;
@end
@implementation OnePerson
- (void)dealloc
{
NSLog(@"OnePerson --- dealloc");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
OnePerson *person = [[OnePerson alloc] init];
void (^block)(void) = ^{
person.age = 10;
};
NSLog(@ "% @", block);
NSLog(@" Block is about to be destroyed");
}
return 0;
}
/// Print the result
2020- 12- 09 20:09:12.955196+0800 BlockDetail[55739:1517434] <__NSMallocBlock__: 0x10040c800>
2020- 12- 09 20:09:12.955546+0800 BlockDetail[55739:1517434] block is about to be destroyed2020- 12- 09 20:09:12.955596+0800 BlockDetail[55739:1517434] OnePerson --- dealloc
Copy the code
The block captured the auto person variable, so it should be NSStackBlock, but since the ARC environment automatically copied to the heap, the Person object was delayed until the block was freed. Why?
Peep bottom source code again:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
OnePerson *__strong person; /// strong reference !!!!!
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, OnePerson *__strong _person, int flags=0) : person(_person) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
The person object is captured, referenced by the __strong modifier, visible, and the heap block is strongly referenced to !!!! for the captured object
Blocks in stack space do not strongly reference captured objects, and blocks in heap space do strongly reference captured objects.
Why don’t stack blocks capture variables by strong references? The block in the stack itself is in danger of being released at any time, so how can you force references to captured variables? That is, the stack block does not retain captured external variables