What is the block
A block object can be understood as a standard C anonymous function with runtime characteristics. You can see why the official documentation calls it an object by looking at its source code.
/* Revised new layout. */ struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); }; struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...) ; struct Block_descriptor *descriptor; /* Imported variables. */ };Copy the code
There are six types of ISA:
/* the raw data space for runtime classes for blocks */
/* class+meta used for stack, malloc, and collectable based blocks */
BLOCK_EXPORT void * _NSConcreteStackBlock[32];
BLOCK_EXPORT void * _NSConcreteMallocBlock[32];
BLOCK_EXPORT void * _NSConcreteAutoBlock[32];
BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32];
BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];
BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32];
Copy the code
In general, we only need to consider three of these:
- _NSConcreteStackBlock (ARC: _NSConcreteMallocBlock)
- _NSConcreteMallocBlock
- _NSConcreteGlobalBlock
How does a block capture a variable
The main concerns here are static and local variables. A (static) global variable is generally not considered special because of its scope.
- A block will only capture the variables it needs and not those it doesn’t.
- Static variables capture a pointer to the variable, so you can change the original value.
- Local variables capture data structures, so the original value cannot be modified.
- When a block captures an Objective-C object, it can modify the object’s internal value (a DTO is understood to be a pointer).
- When a block captures an Ojbective -c object, it cannot modify the object itself. See the following test code for details.
- By adding
__block
, can change the results of 3 and 5.
Test code:
static int variable_static = 0; int variable = 0; __block int varibale_blcok = 0; KFABlockDto *dto1 = [[KFABlockDto alloc] initWithName:@" I am 1"]; KFABlockDto *dto2 = [[KFABlockDto alloc] initWithName:@" I am 2"]; KFABlockDto *dto = dto1; KFABlockDto *dto_c = dto1; __block KFABlockDto *dto_block = dto1; __block KFABlockDto *dto_block_c = dto1; void (^TestBlock)(void) = ^(){ variable_static++; // variable++; // Variable is not assignable (missing __block type specifier) varibale_blcok++; Dto. name = @" dto"; // dto_c = dto2; // Variable is not assignable (missing __block type specifier) dto_block. Name = @" I am dto_block"; dto_block_c = dto2; }; NSLog(@"variable_static: %d\nvaribale_blcok: %d\ndto: %@\ndto_block: %@\ndto_block_c: %@",variable_static,variable_static,dto,dto_block,dto_block_c); TestBlock(); NSLog(@"variable_static: %d\nvaribale_blcok: %d\ndto: %@\ndto_block: %@\ndto_block_c: %@",variable_static,variable_static,dto,dto_block,dto_block_c);Copy the code
The console:
Variable_static: 0 varibale_BLCOk: 0 Dto: Name: I am 1, address: 0x7FFEE8bb51e8 DTO_block: Name: I am 1, address: 0x7FFEE8bb51e8 Name: I am 1, address: 0x7FFEE8bb51e8 Variable_static: 1 varibale_BLCOk: 1 Dto: Name: I am dto_block, address: 0x7FFEE8bb51e8 DTO_block: Name: I'm dto_block, address: 0x7FFee8bb51e8 DTO_block_C: Name: I'm 2, address: 0x7FFEE8bb51e8Copy the code
What does __block do
If we want to change the value of an external variable, we need to use__block
To embellish, that__block
Why does this work? 用__block
After grooming, Mr. Will become a__Block_byref_i_0
Block captures a pointer to the structure of the. This has the effect of changing the external variable.
Why does block use copy
First look at the following test code and print results.
@property (nonatomic, copy) KFABasicParamBlock block_copy; @property (nonatomic, strong) KFABasicParamBlock block_strong; //@property (nonatomic, retain) KFABasicBlock block_retain; // Retain'ed block property does not copy the block - use copy attribute instead @property (nonatomic, assign) KFABasicParamBlock block_assign; @property (nonatomic, weak) KFABasicParamBlock block_weak; ... / / 1 / / void (^ TestBlock) (nsstrings *) = ^ (nsstrings * type) {NSLog (@ "I'm just a % @ the block of type", type); }; // ② int a = 2; Void (^TestBlock)(NSString *) = ^(NSString *type) {NSLog(@"%d I am only a %@ block",a,type); }; self.block_copy = TestBlock; self.block_strong = TestBlock; self.block_assign = TestBlock; self.block_weak = TestBlock; NSLog(@"%@\n%@\n%@\n%@\n%@",TestBlock,self.block_copy,self.block_strong,self.block_assign,self.block_weak); [the self performSelector: @ the selector (testMemory) withObject: nil afterDelay: 2.0]; ______ - (void)testMemory { self.block_copy(@"copy"); self.block_strong(@"strong"); // self.block_assign(@"assign"); // Thread 1: EXC_BAD_ACCESS (code=2, address=0x600000ea5d70) // self.block_weak(@"weak"); // Thread 1: EXC_BAD_ACCESS (code=1, address=0x460) }Copy the code
console:
MRC && ① : TestBlock:<__NSGlobalBlock__: 0x10e7d6c98> block_copy:<__NSGlobalBlock__: 0x10e7d6c98> block_strong:<__NSGlobalBlock__: 0x10e7d6c98> block_assign:<__NSGlobalBlock__: 0x10e7d6c98> block_weak:<__NSGlobalBlock__: 0x10e7d6c98> MRC && ② : TestBlock:<__NSStackBlock__: 0x7ffeed3e2f08> block_copy:<__NSMallocBlock__: 0x6000016ead90> block_strong:<__NSMallocBlock__: 0x6000016eb540> block_assign:<__NSStackBlock__: 0x7ffeed3e2f08> block_weak:<__NSStackBlock__: 0x7ffeed3e2F08 > ARC && ① : TestBlock:<__NSGlobalBlock__: 0x10AF01c90 > block_copy:<__NSGlobalBlock__: 0x10af01c90> block_strong:<__NSGlobalBlock__: 0x10af01c90> block_assign:<__NSGlobalBlock__: 0x10AF01C90 > block_weak:<__NSGlobalBlock__: 0x10af01c90> ARC && ② : TestBlock:<__NSMallocBlock__: 0x6000020f5f80> block_copy:<__NSMallocBlock__: 0x6000020f5f80> block_strong:<__NSMallocBlock__: 0x6000020f5f80> block_assign:<__NSMallocBlock__: 0x6000020f5f80> block_weak:<__NSMallocBlock__: 0x6000020f5f80>Copy the code
__NSGlobalBlock__
Whether ARC or MRC is global, it is not affected by modifiers.- Under MRC, blocks are on the stack when they are created, and are decorated with copy in order to copy them onto the heap. That is
__NSStackBlock__
->__NSMallocBlock__
. With retain, Xcode will warn:
Retain’ed block property does not copy the block – use copy attribute instead
- In ARC, after the block is created, print out that it has become
__NSMallocBlock__
. So strong is the same as copy, but in order to preserve the tradition and avoid some unexpected problems, I prefer to stick with copy.
Blocks “just work” when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more.
reference
- Blocks Programming Topics
- BlocksRuntime
- Transitioning to ARC Release Notes
- Delve into how blocks capture external variables and implement __block
- In-depth study of Block using weakSelf, strongSelf, @Weakify, @Strongify to solve circular reference
- A look inside blocks: Episode 1
- A look inside blocks: Episode 2
- A look inside blocks: Episode 3 (Block_copy)
- Implementation of Objective-C Blocks