Writing in the front

This article explains blocks from the following aspects

  • Basic use of blocks
  • Layout of blocks in memory
  • Block captures and analyzes variables
  • MRC versus ARC
  • __blockThe analysis of the
  • Memory management problems in block. Procedure
  • Circular reference problems caused by blocks

So what is a block let’s talk a little bit about what is a closure. On Wikipedia, closures are defined as:

In programming languages, A closure is a function or reference to a function together with a referencing environment – a table storing a reference to each of the non-local variables (also called free variables or upvalues) of that function.

The translation is:

A closure is a function (or pointer to a function), plus context variables (sometimes called free variables) outside of which the function executes.

  • Blocks are really objective-C implementations of closures.

Basic use of blocks

  • A block is also essentially an OC object with an ISA pointer inside it
  • A block is an OC object that encapsulates a function call and its environment
  • The underlying structure of a block is shown below

No parameters no definition and use of return values

Void (^MyBlockOne)(void) = ^{NSLog(@" no argument, no return value "); }; / / call MyBlockOne ();Copy the code

No parameter has the definition and use of return values

Int (^MyBlockTwo)(void) = ^{NSLog(@" no arguments return value "); // int (^MyBlockTwo)(void) = ^{NSLog(@" no arguments return value "); return 2; }; Int res = MyBlockTwo();Copy the code

Definition and use of return values with or without parameters

Void (^MyBlockThree)(int a) = ^(int a){NSLog(@" there is no return value a = %d",a); }; / / call MyBlockThree (10);Copy the code

The definition and use of parameters and return values

Int (^MyBlockFour)(int a) = ^(int a){NSLog(@" a "a = %d",a); return a * 2; }; MyBlockFour(4);Copy the code

Typedef definition Block

In practice, we often need a block as an attribute, so we can define a block

Eg: Define a block that takes parameters and returns values

typedef int (^MyBlock)(int a, int b);
Copy the code

When you define a property, you can hold the block as follows

@property  (nonatomic,copy) MyBlock myBlockOne;
Copy the code

Block implementation

self.myBlockOne = ^int(int a, int b) {
 return a + b;
};
Copy the code

call

self.myBlockOne(2, 5);
Copy the code

Block types and data structures

Block data structure analysis

Generate CPP files

The following code

int age = 20;
void (^block)(void) =  ^{
 NSLog(@"age is %d",age);
 };

block();
Copy the code
  • Open the terminal and CD to the current directory

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

To generate the main CPP

Block structure analysis

int age = 20; / / block definition of void block (*) (void) = ((void (*) ()) & __main_block_impl_0 (__main_block_func_0 (void *), &__main_block_desc_0_DATA, age)); / / block call ((void (__block_impl *) (*)) ((__block_impl *) block) - > FuncPtr) ((__block_impl *) block);Copy the code

The above code removes some of the cast code and is left with something like this

int age = 20; void (*block)(void) = &__main_block_impl_0( __main_block_func_0, &__main_block_desc_0_DATA, age ); FuncPtr(FuncPtr);Copy the code

Block is a struct object, __main_block_IMPL_0

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, struct __main_block_desc_0 *desc, int _age, int flags=0) : Age (_age) {//isa points to _NSConcreteStackBlock indicating that this block is of type impl. Isa = &_NSConcretestackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

The first struct __block_impl impl;

struct __block_impl {
 void *isa;
 int Flags;
 int Reserved;
 void *FuncPtr;
};
Copy the code

The second struct is __main_block_desc_0;

static struct __main_block_desc_0 { size_t reserved; size_t Block_size; // struct __main_block_impl_0 size of memory}Copy the code
The third structure is' age ', the captured local variable 'age' __main_block_func_0 'Copy the code
Static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_920c4yq936b63mvtj4wmb32m0000gn_T_main_7f3f1b_mi_0,age); }Copy the code

Let me draw it as a picture

Variable to capture

In fact, the above code we can already see the variable capture, here continue to analyze in detail

Local variable auto

  • The local variables that we normally write, by default, have auto.

Run the code

For example, the following code

int age = 20;
void (^block)(void) =  ^{
 NSLog(@"age is %d",age);
};
age = 25;

block();
Copy the code

Is equivalent to

auto int age = 20;
void (^block)(void) =  ^{
 NSLog(@"age is %d",age);
};
age = 25;

block();
Copy the code

The output

20

Analysis of the

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

To generate the main CPP

As is shown in

int age = 20;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
age = 25;

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;

NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_920c4yq936b63mvtj4wmb32m0000gn_T_main_d36452_mi_5);
Copy the code

__main_block_IMPL_0 (age = 25); __main_block_impl_0 (age = 25)

Local variable static

Static modified local variables are not destroyed

Run the code

eg

static int height  = 30;
int age = 20;
void (^block)(void) =  ^{
 NSLog(@"age is %d height = %d",age,height);
};
age = 25;
height = 35;
block();
Copy the code

The execution result is

age is 20 height = 35
Copy the code

As you can see, changing height outside the block still affects the value inside the block

Analysis of the

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

To generate the main CPP

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
 int age = __cself->age; // bound by copy
 int *height = __cself->height; // bound by copy

 NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_920c4yq936b63mvtj4wmb32m0000gn_T_main_3146e1_mi_4,age,(*height));
 }

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; 

 static int height = 30;
 int age = 20;
 void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height));
 age = 25;
 height = 35;
 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
Copy the code

So as you can see, age is passed directly, height is passed *height so you’re just passing in the memory address and changing it.

The global variable

Run the code

int age1 = 11;
static int height1 = 22;

int main(int argc, const char * argv[]) {
 @autoreleasepool {
 void (^block)(void) =  ^{
 NSLog(@"age1 is %d height1 = %d",age1,height1);
 };
 age1 = 25;
 height1 = 35;
 block();

 }
 return 0;
}
Copy the code

The output is

age1 is 25 height1 = 35
Copy the code

Analysis of the

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m

To generate the main CPP

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; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_920c4yq936b63mvtj4wmb32m0000gn_T_main_4e8c40_mi_4,age1,height1); } 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 (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); age1 = 25; height1 = 35; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } return 0; }Copy the code

As you can see from the CPP file, the global variables age1 and Height1 are not captured. When accessing the global variables age1 and Height1, they are accessed directly

summary

  • Auto modifies local variables that are passed in value
  • Static modifier local variable that is pointer passed

It makes sense, too, because local variables decorated with auto are destroyed when they leave scope. If it is passed as a pointer, the variable may already be destroyed by the time it is accessed. The program will go wrong. Global variables are accessible everywhere, so there is no need to capture them.

Block type

Block is also an OC object

Before we look at block types, let’s clarify the concept that a block with a pointer to ISA is an OC object, as shown in the following code

void (^block)(void) =  ^{
 NSLog(@"123");
};

NSLog(@"block.class = %@",[block class]);
NSLog(@"block.class.superclass = %@",[[block class] superclass]);
NSLog(@"block.class.superclass.superclass = %@",[[[block class] superclass] superclass]);
NSLog(@"block.class.superclass.superclass.superclass = %@",[[[[block class] superclass] superclass] superclass]);
Copy the code

The output is

iOS-block[18429:234959] block.class = __NSGlobalBlock__ iOS-block[18429:234959] block.class.superclass = __NSGlobalBlock  iOS-block[18429:234959] block.class.superclass.superclass = NSBlock iOS-block[18429:234959] block.class.superclass.superclass.superclass = NSObjectCopy the code

The block type in the above code is __NSGlobalBlock, and the inheritance relationship can be expressed as __NSGlobalBlock__ : __NSGlobalBlock: NSBlock: NSObject

There are three types of blocks

There are three types of blocks, which you can see by calling the class method or isa pointer. They are all derived from the NSBlock type

  • __NSGlobalBlock__ (_NSConcreteGlobalBlock)
  • __NSStackBlock__ (_NSConcreteStackBlock)
  • __NSMallocBlock__ (_NSConcreteMallocBlock)

The three different types and environments correspond as follows

Its memory allocation is as follows

Run code view

Under the MRC

Note that the following code is tested under MRC

Note that the following code is tested under MRC

Note that the following code is tested under MRC

Because the compiler does a lot of optimizations with ARC, it often doesn’t see the essence,

  • Change to MRC method:Build SettingsThe inside of theAutomatic Reference CountingChange to the NO

As shown in the figure belowLet me write it in code

void (^block)(void) = ^{ NSLog(@"123"); }; NSLog(@" no access to auto block.class = %@",[block class]); auto int a = 10; void (^block1)(void) = ^{ NSLog(@"a = %d",a); }; NSLog(@" access auto block1.class = %@",[block1 class]); NSLog(@" Access volume auto and copy Block1-copy. Class = %@",[[block1 class] copy]);Copy the code

The output is

Os-block [23542:349513] does not access Auto Block. class = __NSGlobalBlock__ ios-block [23542:349513] accesses Auto Block1. class = __NSStackBlock__ ios-block [23542:349513] Access Volume Auto and copy block1-copy. Class = __NSStackBlock__Copy the code

And you can see thatIs consistent with the

Under the ARC

Under ARC, the output from the above code looks like the following because the compiler has copied it

Ios-block [24197:358752] does not access Auto Block. class = __NSGlobalBlock__ ios-block [24197:358752] accesses Auto Block1. class = __NSMallocBlock__ ios-block [241971:358752] Access Volume Auto and copy Block1-copy. Class = __NSMallocBlock__Copy the code

Block of copy

As mentioned earlier, in an ARC environment, the compiler automatically copies blocks on the stack to the heap as appropriate, as in the following cases

The copy of

  • Block as a function return value
  • Assigns a block to a __strong pointer
  • Block is used as a Cocoa API method name containing a method parameter called usingBlock
  • Block as a method parameter of the GCD API

Block as a function return value

// define Block typedef void (^YZBlock)(void); YZBlock myblock() {int a = 6; return ^{ NSLog(@"--------- %d",a); }; } YZBlock Block = myblock(); Block(); NSLog(@" [Block class] = %@", [Block class]);Copy the code

The output is

iOS-block[25857:385868] --------- 6
iOS-block[25857:385868]  [Block class] = __NSMallocBlock__
Copy the code

If the above code output __NSStackBlock__ under MRC, in ARC, automatically copy, so __NSMallocBlock__

Assign a block to__strongWhen the pointer

// define Block typedef void (^YZBlock)(void); int b = 20; YZBlock Block2 = ^{ NSLog(@"abc %d",b); }; NSLog(@" [Block2 class] = %@", [Block2 class]);Copy the code

The output is

iOS-block[26072:389164]  [Block2 class] = __NSMallocBlock__
Copy the code

If the above code output __NSStackBlock__ under MRC, in ARC, automatically copy, so __NSMallocBlock__

Block is used as a Cocoa API method name containing a method parameter called usingBlock

eg

NSArray *array = @[@1,@4,@5];
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
 // code
}];
Copy the code

Block as a method parameter of the GCD API

eg

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

}); 

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
 //code to be executed after a specified delay
});
Copy the code

Recommended writing method of block attribute under MRC

  • @property (copy, nonatomic) void (^block)(void);

Recommended way to write block properties under ARC

  • @property (strong, nonatomic) void (^block)(void);
  • @property (copy, nonatomic) void (^block)(void);

[address] (zhuanlan.zhihu.com/p/112406399…).

If there is something wrong, please point out, welcome active discussion! Hope to see the article feel good point support!!