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:

  1. _NSConcreteStackBlock (ARC: _NSConcreteMallocBlock)
  2. _NSConcreteMallocBlock
  3. _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.

  1. A block will only capture the variables it needs and not those it doesn’t.
  2. Static variables capture a pointer to the variable, so you can change the original value.
  3. Local variables capture data structures, so the original value cannot be modified.
  4. When a block captures an Objective-C object, it can modify the object’s internal value (a DTO is understood to be a pointer).
  5. When a block captures an Ojbective -c object, it cannot modify the object itself. See the following test code for details.
  6. 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__blockTo embellish, that__blockWhy does this work?__blockAfter grooming, Mr. Will become a__Block_byref_i_0Block 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
  1. __NSGlobalBlock__Whether ARC or MRC is global, it is not affected by modifiers.
  2. 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

  1. 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