In iOS, there are three types of blocks.

(1) a global static block, which does not access any external variables, is destroyed upon execution.

^{ NSLog(@"Hello World!" ); } ();Copy the code

(2) Blocks stored on the stack are destroyed when the function returns. The difference is that external variables are called.

 [UIView animateWithDuration:3 animations:^{  
             self.view.backgroundColor = [UIColor redColor];  
  }];
Copy the code

(3) Blocks stored in the heap are destroyed when the reference count reaches zero. The button click event, for example, is always there, and even if it’s executed, it’s not destroyed, because the button can still be clicked, and the View holding the button is destroyed until it’s destroyed.

#import <UIKit/UIKit.h> 
typedef void(^ButtonClickBlcok)();
@interface TestView : UIView 
@property (nonatomic, copy) ButtonClickBlcok buttonClickBlcok;
@end

#import "TestView.h"
@implementation TestView 
- (IBAction)buttonClick:(id)sender {        
if (self.buttonClickBlcok) {      
  self.buttonClickBlcok();    
}}
@end
Copy the code

Block capture variable

Normal local variables default to the auto modifier (automatic variables) to be destroyed when out of scope.

1. Local variable auto

int age = 20; Int age = 20; Void (^block)(void) = ^{NSLog(@"age is %d",age); }; age = 25; block(); / / callCopy the code

When it runs, it prints 20. If you pass the age value 20 to a block and change the age value later, you cannot change the age value in the block.

  • Conclusion: The local variable modified by auto is value passing.

It makes sense, too, because local variables decorated with auto are destroyed when they leave scope.

2. Local variable static

Static modified local variables are not destroyed

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

When it runs, it prints 25. As you can see, the age value in the block changes with the static modifier.

  • Conclusion: Static modifies local variables that are pointer passed.

Pointer passing can cause access when the variable has been destroyed, and the program will have problems.

3. Global variables

int age1 = 11; static int height1 = 22; . void (^block)(void) = ^{ NSLog(@"age1 is %d height1 = %d",age1,height1); }; age1 = 31; height1 = 33; block(); // Call print: age1 is 31 height1 = 33Copy the code

In this case, no global variables are captured. When it comes to access, it is directly accessed without capturing at all.

Global variables are inherently accessible everywhere, so there is no need to capture them.

The keyword __block

The above discussion is about modifying the value of a captured variable outside of a block. What if you need to change the value of a captured variable within a block? There are three ways to modify local variables.

1. Write local variables as global variables

That is, the third case mentioned above is no longer redundant.

A global variable is accessible everywhere, and its memory address can be manipulated directly within a block. After the block is called, the value of the address to which the global variable points has been changed.

2. Define local variables as static

In other words, the second case mentioned above is no longer redundant.

When a local variable is static, the block internally captures the address of the variable. That way, of course, local variables can be modified inside the block.Copy the code

Although the above two methods can achieve the purpose of modifying local variables inside the block, the disadvantage is that the variables cannot be destroyed in time and will remain in memory. And a lot of times, we just need to use it temporarily, and when we don’t use it, we can destroy it. The third method, __block, is needed.

3. Keyword __block

Modify local variables with __block.

__block int age = 20; void (^block)(int a) = ^{ age = a; NSLog(@"age is %d",age); }; block(33); // Call to print age is 33Copy the code

Note: __block cannot modify global variables, static variables

— > Explore the underlying __block:

Since the __block variable age is captured, the underlying structure of the block is:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_age_0 *age; __Block_byref_age_0 struct *_age flags __main_block_IMPL_0 (void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; //fp is the function address Desc = Desc; }};Copy the code

__Block_byref_age_0 structs are found instead of blocks.

struct __Block_byref_age_0 { void *__isa; //isa pointer __Block_byref_age_0 *__forwarding; // a pointer to itself int __flags; int __size; int age; // Use the value};Copy the code

This structure is due to the __block variable. The second __forwarding holds Pointers to itself, and the fifth age holds local variables.

  • The underlying principle and logic of __block:

    The compiler will take__blockA variable is wrapped as an object. When calling a variable, according to__Block_byref_age_0In the__forwarding, find the memory in which the variable age is located, and modify the value.

Block accesses the OC object

All of the above are block access variables. What if the OC object is accessed?

NSObject *obj = [[NSObject alloc] init]; void (^block)(void) = ^{ NSLog(@"%@",obj); }; block( ); / / callCopy the code

In this case, the structure of the block looks like this

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  // ——>
  NSObject *__strong obj;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__strong _obj, int flags=0) : obj(_obj) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
Copy the code

Internally, it is strongly or weakly referenced based on the modifiers in your code (__strong, __weak, __unsafe_unretained).

  • Blocks of heap space to capture it__blockVariables form strong references.

Therefore, local variables on the stack can be destroyed at any time. But for __block modified local variables, there is a strong reference.

Blocks on the stack do not generate strong references to any captured variables.

Block circular reference problem

typedef void (^YZBlock) (void);

@interface YZPerson : NSObject

@property (copy, nonatomic) YZBlock block;
@property (assign, nonatomic) int age;

@end

{
    YZPerson *person = [[YZPerson alloc] init];
    person.age = 10;
    person.block = ^{
         NSLog(@"person.age--- %d",person.age);
    }; 
}
Copy the code

When the program ends, the person is not released, causing a memory leak.

Reason: Blocks are automatically copied to the heap (and the variable person inside the block is also copied to the heap) and block strongly references Person. A block is a property of Person, and person strongly references the block. Strong references to each other, no one can release.

Resolving circular references

There are 3 ways to solve this problem:

1. Keyword __weak
{
    YZPerson *person = [[YZPerson alloc] init];
    person.age = 10;
    
    __weak YZPerson *weakPerson = person;
    person.block = ^{
         NSLog(@"person.age--- %d",  weakPerson.age);
    }; 
}
Copy the code

When the local variable disappears, in the case of YZPseson, there’s only one weak pointer to it, it’s destroyed, and then the block is destroyed.

2. Keyword __unsafe_unretained

Also similar to the previous, can solve the circular reference. However, it is not safe. When the object to which the pointer points is destroyed, the address stored in the pointer remains the same.

3. Keyword __block
__block YZPerson *person = [[YZPerson alloc] init]; person.block = ^{ NSLog(@"person.age--- %d",person.age); person = nil; };Copy the code

The essence of this method is to change the value of a local variable inside a block.

Under ARC, the above three ways are compared, the best is __weak. Under MRC, __unsafe_unretained or __block can be used only because weak Pointers are not supported.

Small example

void test1() { int a = 10; void (^block) () = ^{ NSLog(@"a is %d",a); } a = 20; block(); //a = 10 } void test2() { __block int a = 10; void (^block) () = ^{ NSLog(@"a is %d",a); } a = 20; block(); // a = 20 } void test3() { static int a = 10; void (^block) () = ^{ NSLog(@"a is %d",a); } a = 20; block(); //a = 20 } int a = 10; void test4() { void (^block) () = ^{ NSLog(@"a is %d",a); }; a = 20; block(); / / 20}Copy the code

Why is it possible to modify external variables by adding __block?

As we all know, blocks do not allow you to change the value of an external variable, which is the memory address of the pointer on the stack. All __block does is put the memory address of the “external variable” in the stack into the heap whenever it is observed that the variable is held by the block. In turn, you can modify the values of external variables inside the block.