The type of the Block
-
There are three types of blocks:
- NSGlobalBlock (NSConcreteGlobalBlock)
- NSStackBlock (NSConcreateStackBlock)
- NSMallocBlock (NSConcreateMallocBlock)
-
You can see the type through a class or ISA pointer, which ultimately inherits from the NSBlock type
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"Hello");
};
NSLog(@"% @", [block class]);
NSLog(@"% @", [[block class] superclass]);
NSLog(@"% @", [[[block class] superclass] superclass]);
NSLog(@"% @", [[[[block class] superclass] superclass] superclass]);
}
return 0;
}
Copy the code
2020-06-03 21:42:23.579757+0800 Interview01-block nature [56222:9306988] __NSGlobalBlock__ 2020-06-03 21:42:23.581321+0800 [56222:9306988] __NSGlobalBlock 2020-06-03 21:42:23.581831+0800 Interview01-block [56222:9306988] NSBlock 2020-06-03 21:42:23.585482+0800 Interview01-Block essence [56222:9306988] NSObjectCopy the code
Because it inherits from NSObject, blocks are isa Pointers, so you can also think of a block as an object
Print and compile the three blocks
Int main(int argc, const char * argv[]) {@autoreleasepool { Void (^block1)(void) = ^{NSLog(@"Hello");
};
int age = 10;
void (^block2)(void) = ^{
NSLog(@"Hello - %d", age);
};
NSLog(@"% @ % @ % @", [block1 class], [block2 class], [^{
NSLog(@"%d", age);
} class]);
return0; }}Copy the code
Print the result
2020-06-03 21:48:28.625843+0800 Interview01-block nature [56274:9310988] __NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__Copy the code
$xcrun – SDK iphoneOS clang –arch arm64 rewrite-objc main.m
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; }}; struct __main_block_impl_1 { struct __block_impl impl; struct __main_block_desc_1* Desc; int age; __main_block_impl_1(void *fp, struct __main_block_desc_1 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; struct __main_block_impl_2 { struct __block_impl impl; struct __main_block_desc_2* Desc; int age; __main_block_impl_2(void *fp, struct __main_block_desc_2 *desc, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
Notice that this is all _NSConcreteStackBlock. It’s not the same as the runtime, but everything is the result of the runtime, because the runtime may have done some processing, and the compiled code may not be real OC code
Block memory allocation
Application memory addresses are sorted from low to high (highest stack)
- .text segment: code
- .data segment: global variable
- Heap: dynamically allocates memory, programmers apply for memory, and manages memory
- Stack: local variables that are destroyed when braces are left
int age_ = 10;
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
NSLog(@"Data segment: age_ %p", &age_);
NSLog(@"Stack: a %p", &a);
NSLog(@"Heap: obj %p", [[NSObject alloc] init]);
NSLog(@"Segment: class %p", [RLPerson class]);
}
return 0;
}
Copy the code
Print result:
2020-06-29 14:09:54.578086+0800 TestBlock[3428:10823423] 0x1000011D0 2020-06-29 14:09:54.578513+0800 TestBlock[3428:10823423] stack A 0x7ffeefbff51c 2020-06-29 14:09:54.578605+0800 TestBlock[3428:10823423] Obj 0x100502F80 2020-06-29 14:09:54.578705+0800 TestBlock[3428:10823423] Data segment: 0x1000011a8Copy the code
You can see that the RLPerson address is similar to the age_ address, so the class is stored in the data segment
The Block under the MRC
Disable ARC: Target -> Build Setting -> Objctive -c Automatic Reference Counting set to NO
Check to see if the block type of the auto variable is accessed
Int main(int argc, const char * argv[]) {@autoreleasepool {// Global: no access to auto void (^block1)(void) = ^{NSLog(@"block1---------"); }; // Stack: access the auto variable int age = 10; void (^block2)(void) = ^{ NSLog(@"block2---------%d", age);
};
NSLog(@"% @ - % @", [block1 class], [block2 class]);
}
return 0;
}
Copy the code
Print result:
[56499:9322480] __NSGlobalBlock__-__NSStackBlock__Copy the code
You can see the presence of __NSStackBlock__ of type __NSStackBlock__ that accesses the auto variable
void (^block)(void);
void test2()
{
// NSStackBlock
int age = 10;
block = ^{
NSLog(@"block---------%d", age);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test(2); block(); }return 0;
}
Copy the code
Print result:
2020-06-03 22:08:45.533995+0800 Interview01-Block essence [56553:9325730] Block ----------272632888Copy the code
The value of age is incorrect. After test2() is executed, the stack memory may be garbage, causing the printed value to be problematic
How to deal with this situation? Change the block to _NSMallocBlock, where __NSStackBlock__copy becomes _NSMallocBlock_. You can control the block’s life cycle in the heap.
void (^block)(void);
void test2()
{
// NSStackBlock
int age = 10;
block = [^{
NSLog(@"block---------%d", age);
} copy];
[block release];
}
Copy the code
Print the result
2020-06-03 22:13:27.853082+0800 Interview01-Block essence [56639:9329121] Block ---------10Copy the code
summary
Block in ARC environment
In an ARC environment, the compiler automatically copies blocks on the stack to the heap as appropriate, as in the following case
- As a function return value
- When block is assigned to the strong pointer _strong
- Block is used as a Cocoa API method name that contains the usingBlock method argument
- Block is a method parameter of the GCD API
As a function return value
typedef void (^RLBlock)(void);
RLBlock myblock()
{
int a = 10;
return ^{
NSLog(@"---------%d", a);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"% @", [myblock() class]);
}
return 0;
}
Copy the code
2020-06-03 23:26:53.399930+0800 Interview01-block的copy[57002:9364496] __NSMallocBlock__
Copy the code
- When block is assigned to the strong pointer _strong
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
RLBlock block = ^{
NSLog(@"---------%d", age);
};
NSLog(@"Strong pointer :%@", [block class]);
NSLog(@"No strong pointer :%@", [^{
NSLog(@"---------%d", age);
} class]);
}
return 0;
}
Copy the code
A strong pointer to __NSMallocBlock__ 2020-06-03 23:29:30.468301+0800 Interview01-block copy[57030:9366298] No strong pointer to __NSStackBlock__Copy the code
- Block is used as a Cocoa API method name with usingBlock. NSMutableArray is not modified by __block
NSMutableArray *sorts = [NSMutableArray array];
[sorts enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
Copy the code
- Block is a method parameter of the GCD API
Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{});Copy the code
The auto variable of object type
Create a class
@interface RLPerson : NSObject
@property(nonatomic, assign) NSInteger age;
@end
@implementation RLPerson
- (void)dealloc
{
// [super dealloc];
NSLog(@"RLPerson - dealloc");
}
@end
typedef void (^RLBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
RLBlock block;
{
RLPerson *person = [[RLPerson alloc] init];
person.age = 10;
block = ^{
NSLog(@"---------%ld", (long)person.age);
};
NSLog(@"-- -- -- -- -- - % @", [block class]);
}
NSLog(@"-- -- -- -- --");
}
return 0;
}
Copy the code
Because of strong pointer references, all blocks are of type __NSMallocBlock__
Print the value at the break point to NSLog(@”—–“) in the ARC environment
2020-06-05 08:45:09.755390+0800 Interview01-block的copy[1125:71486] ------__NSMallocBlock__
Copy the code
In the MRC environment, the value is printed at the interrupt point to NSLog(@”—–“)
- (void)dealloc
{
[super dealloc];
NSLog(@"RLPerson - dealloc");
}
typedef void (^RLBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
RLBlock block;
{
RLPerson *person = [[RLPerson alloc] init];
person.age = 10;
block = ^{
NSLog(@"---------%ld", (long)person.age);
};
[person release];
NSLog(@"-- -- -- -- -- - % @", [block class]);
}
NSLog(@"-- -- -- -- --");
}
return 0;
}
Copy the code
2020-06-07 12:15:50.813657+0800 Interview01-block copy[1231:28255] RLPerson - dealloc 2020-06-07 12:16:10.955578+0800 Interview01 - block copy (1231-28255) -- -- -- -- -- - __NSStackBlock__Copy the code
Copy blocks in the MRC environment
block = [^{
NSLog(@"---------%d", person.age);
} copy];
Copy the code
Print the result
2020-06-07 12:18:58.412416+0800 Interview01-block的copy[1268:31279] ------__NSMallocBlock__
Copy the code
The block is of type __NSMallocBlock__, which is equivalent to [Person retain], so heap blocks can strongly reference holding objects, whereas stack blocks cannot
ARC continues the research by adding an __weak modifier to the person object
int main(int argc, const char * argv[]) {
@autoreleasepool {
RLBlock block;
{
RLPerson *person = [[RLPerson alloc] init];
person.age = 10;
__weak RLPerson *weakPerson = person;
block = ^{
NSLog(@"---------%ld", (long)weakPerson.age);
};
NSLog(@"-- -- -- -- -- - % @", [block class]);
}
NSLog(@"-- -- -- -- --");
}
return 0;
}
Copy the code
Break point to NSLog(@”—–“); Print the result and find that the Person has been destroyed
2020-06-07 12:38:46.093930+0800 Interview01-block copy[1540:52404] RLPerson - Dealloc 2020-06-07 12:38:46.094325+0800 Interview01 - block copy (1540-52404) -- -- -- -- -- - __NSMallocBlock__Copy the code
View the c++ source code execution command at this point
$ xcrun -sdk iphoneos clang -arch -rewrite-objc main.m
Copy the code
Errors will be found
/var/folders/zz/v5rdxc250h53xq3b41vnsd3c0000gn/T/main-802857.mi:28844:28: error:
cannot create __weak reference because the current deployment target does
not support weak references
__attribute__((objc_ownership(weak))) RLPerson *weakPerson = person;
^
1 error generated.
Copy the code
This is because you want to compile at runtime, not statically, using the following command
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13 main.m
Copy the code
RLPerson *__weak weakPerson
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; RLPerson *__weak weakPerson; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, RLPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
If block is changed to print person
block = ^{
NSLog(@"---------%d", person.age);
};
Copy the code
RLPerson *__strong person;
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; RLPerson *__strong person; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, RLPerson *__strong _person, int flags=0) : person(_person) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
Copy operation to capture block variables under MRC
The __main_block_desc_0 structure of c++ source block is changed internally
1. Capture the Auto object
int main(int argc, const char * argv[]) {
@autoreleasepool {
RLBlock block;
{
RLPerson *person = [[RLPerson alloc] init];
person.age = 10;
block = [^{
NSLog(@"---------%d", person.age);
} copy];
}
NSLog(@"-- -- -- -- -- -");
}
return 0;
}
Copy the code
C + + source code
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};
Copy the code
2. Copy the auto object
int main(int argc, const char * argv[]) {
@autoreleasepool {
RLBlock block;
{
RLPerson *person = [[RLPerson alloc] init];
person.age = 10;
block = [^{
NSLog(@"---------%d", person.age);
} copy];
}
NSLog(@"-- -- -- -- -- -");
}
return 0;
}
Copy the code
View the source code
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/); } static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 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};Copy the code
3. Capture the copy operation of the auto basic data type
int main(int argc, const char * argv[]) {
@autoreleasepool {
RLBlock block;
{
int age = 10;
block = ^{
NSLog(@"---------%d", age);
};
}
NSLog(@"-- -- -- -- -- -");
}
return 0;
}
Copy the code
View the source code
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, when the block accesses the auto object type, Desc uses two more copy and dispose methods, which call the _Block_object_assign function. The copy operation does a strong reference based on the modifier preceding the variable. Dispose calls the _Block_object_dispose function to release the device
summary
- When the block internally accesses the auto variable of the object type
- If the block is on the stack, there is no strong reference to the auto variable
- If a block is copied to the heap: Copy calls _Block_object_assign to the internal copy function that uses the block. _Block_object_assign requires retained or weak references based on the auto modifier (_strong, _weak, and _unsafe_unretained)
- If a block is removed from the heap: The internal dispose function that uses the block calls the _Block_object_dispose function. The _Block_object_dispose function automatically releases the referenced auto variable (release)