[iOS-Block summary]

Preface

The blog mainly focuses on summarizing and is updated at any time.

2. Block

Anonymous functions with automatic variables (local variables)

2.1 Block usage specifications

The complete format of Block is: variable declaration + definition:
Please add image description,

int(^sumBlk)(int num1, int num2) = ^int(int a, int b) {
    
    
        return a + b;
    };

Block variables are similar to function pointers,

  • Purpose: automatic variables (local variables)
    • Function parameters
    • static variable
    • static global variables
    • global variables

Intercepting automatic variables : Automatic variable values ​​appear in Block as "intercepting automatic variable values".

Noteworthy point: In current Blocks, the method of intercepting automatic variables does not implement the interception of C language arrays.

2.2 __block modifier

Blocks can intercept variables, but the values ​​of variables cannot be modified in the block .

At this time, we use __blockmodifiers to modify variables, and use modifiers to modify variables that need to blockbe assigned values ​​to ensure that variables can be assigned values.

// blk不允许修改截获变量的值,需要的时候加上__block修饰符即可
    id tstArray = @[@"blk", @"不允许赋值", @"给截获的变量"];
    __block id array = [[NSMutableArray alloc] init];
    void (^blk)(void) = ^{
    
    
        id obj = [[NSObject alloc] init];

//        [array addObject:obj];
        array = tstArray;
    };

2.3 Types of Blocks

blockDivided into global block, 堆 block, and 栈 block
block classifications are briefly summarized as follows

  • A. No external variables are referenced— blockstored in the global area
  • B. External variables are referenced - those explicitly declared as weaktypes blockare stored in the stack area, otherwise they exist in the heap area, which means blockthey are strongof type.
  • NSGlobalBlock:
    There are no external variables referenced inside the block. Block types are all NSGlobalBlock types. They are stored in the global data area and their memory is managed by the system. The retain, copy, and release operations are invalid. This is also the case if external static or global variables are accessed.
  • NSStackBlock:
    External variables are accessed, but there is no strong reference pointing to this block, such as the block printed directly
  • NSMallocBlock:
    In the ARC environment, as long as external variables are accessed and there is a strong reference pointing to the block (or as a function return value), the __NSStackBlock type will be automatically copied to the heap.

2.4 Block circular reference and solution

Introduction of circular reference scenarios

Circular reference means that the object cannot be released normally due to the object being held by the party, and memory leaks will occur.

Strong references to each other in BLock may cause circular references.

typedef void(^TBlock)(void);

@interface ViewController ()
@property (nonatomic, strong) TBlock block;
@property (nonatomic, copy) NSString *name;
@end

@implementation ViewController

- (void)viewDidLoad {
    
    
    [super viewDidLoad];

    // 循环引用
    self.name = @"Hello";
    self.block = ^(){
    
    
        NSLog(@"%@", self.name);
    };
    self.block();
}

Here self holds block, and block holds self, resulting in a circular reference.
As long as one party does not make a strong reference, the circular reference can be released

- (void)viewDidLoad {
    
    
    [super viewDidLoad];

    // 循环引用
    self.name = @"Hello";

    void(^block)(void);
    block = ^(){
    
    
        NSLog(@"%@", self.name);
    };
    block();
}

Why is there no circular reference in this case? Because the current self, that is, ViewController, does not hold the block strongly, the life cycle of the block is only within the viewDidLoad method. After the viewDidLoad method is executed, the block will be released.

Resolve circular references

  1. __weak Because __weak does not perform a reference counting operation on the object, that is, it does not perform a strong reference.
  2. The above case is modified to weak to avoid circular references as follows:
    // 循环引用
    self.name = @"Hello";
    __weak typeof(self) weakSelf = self;
    self.block = ^(){
    
    
        NSLog(@"%@", weakSelf.name);
    };
    self.block();

At this time, self holds block, and block weakly refers to self. The weak reference will automatically become nil, and the strong holding is interrupted, so it will not cause a circular reference. However, this method may have the problem of being released midway (manual delay, the name may have been released when self.name is called). If self is destroyed, the block cannot obtain the name.

  1. The dance of strength and weakness:
    self.name = @"Hello";

    __weak typeof(self) weakSelf = self;
    self.block = ^(){
    
    
        __strong __typeof(weakSelf)strongWeak = weakSelf;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
    
            NSLog(@"%@", strongWeak.name);
        });
    };
    self.block();

Completely solves the above problem of self being released midway.
Principle: The dealloc method is called after completing the operations in the block. After adding strongWeak, the holding relationship is: self -> block -> strongWeak -> weakSelf -> self.

If weakSelf is strongly referenced, it will not be automatically released, because strongWeak is only a temporary variable, and its declaration period is only within the block. After the block is executed, strongWeak will be released, and the weak reference weakSelf will also be automatically released.

  1. Manual interruption
    self.name = @"Hello";

    __block ViewController * ctrl = self;
    self.block = ^(){
    
    
        __strong __typeof(weakSelf)strongWeak = weakSelf;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
    
            NSLog(@"%@", ctrl.name);
            ctrl = nil;
        });
    };
    self.block();

After using ctrl, the holding relationship is: self -> block -> ctrl -> self. ctrl is empty after the block is used. At this point, the block's hold on self is released, and does not constitute a circular reference.

  1. Parameter form solves circular reference block parameter transfer (pointer copy)
    // 循环引用
    self.name = @"Hello";
    self.block = ^(ViewController * ctrl){
    
    
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
    
            NSLog(@"%@", ctrl.name);
        });
    };
    self.block(self);

Insert self into the block as a parameter and copy the pointer without holding self.

Block circular reference scenario

Static variable holds

    // staticSelf_定义:
    static ViewController *staticSelf_;

    - (void)blockWeak_static {
    
    
        __weak typeof(self) weakSelf = self;
        staticSelf_ = weakSelf;
    }

Although weakSelf is a weak reference, staticSelf_ is a static variable and holds weakSelf. staticSelf_ cannot be released, so weakSelf cannot be released either! Causes circular reference!

2.5 Implementation and essence of Block

The implementation of BLock is based on pointers and function pointers. The Block attribute is a pointer to a structure.

	//这是正常的block流程
    void(^myBlock)(void) = ^{
    
    
        printf("myBlock");
    };
    myBlock();

Get the source code definition of the Block structure (C++):

    void(*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

The block syntax in the initialization definition part becomes: &__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
The process of calling block becomes:(__block_impl *)myBlock)->FuncPtr

2.5.1 Initialization part

The initialization part is the Block structure

//Block结构体
struct __main_block_impl_0 {
    
    
  struct __block_impl impl;//impl:Block的实际函数指针,就是指向包含Block主体部分的__main_block_func_0结构体
  struct __main_block_desc_0* Desc;//Desc指针,指向包含Block附加信息的__main_block_desc_0()结构体
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    
    //__main_block_impl_0:Block构造函数(可以看到都是对上方两个成员变量的赋值操作)
    impl.isa = &_NSConcreteStackBlock; // 
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

The __main_block_impl_0 structure, which is the Block structure, contains three parts:

  • Member variable impl
  • Member variable Desc pointer
  • __main_block_impl_0 constructor

struct __block_impl structure : a structure containing the actual function pointer of Block

struct __block_impl {
    
    
  void *isa;//用于保存Block结构体的实例指针
  int Flags;//标志位
  int Reserved;//今后版本升级所需的区域大小
  void *FuncPtr;//函数指针
};
  • _block_implContains the actual function pointer FuncPtr of the Block. The FuncPtr pointer points to the main part of the Block, which is the part of the Block corresponding to ^{...} in the OC code.
  • It also contains flags, which may be used when implementing the internal operations of the block.
  • Area size required for future version upgradesReserved
  • __block_implstructure instance pointerisa

struct __main_block_desc_0 structure:

Block additional information structure: contains the size of the area required for future version upgrades, the size of the Block

static struct __main_block_desc_0 {
    
    
  size_t reserved;//今后版本升级所需区域大小
  size_t Block_size;//Block大小
} __main_block_desc_0_DATA = {
    
     0, sizeof(struct __main_block_impl_0)};

Block constructor __main_block_impl_0 :

As a constructor, note that it has the same name as the Block structure.
Responsible for initializing the member variables of the __main_block_impl_0 structure (that is, the Block structure struct __block_impl)

  //可以看到里面都是一些赋值操作
  __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;
  }

2.5.2 Calling part

  • function prototype((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);

Parse this code step by step:

  1. ((__block_impl *)myBlock)->FuncPtr: This part myBlockconverts to __block_implpointer type and accesses FuncPtrmembers. It gets the function pointer stored inside the block implementation.
  2. ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr): Here, the function pointer is converted to a function type that accepts a __block_impl*parameter of type and returns it void. It converts the function pointer to the actual function type that can be called.
  3. ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock): Finally, myBlockthe resulting function pointer is called using as argument. It calls the function using the block implementation object.

To summarize, this code gets the function pointer stored inside the block implementation and then calls the function, passing the block implementation object itself as a parameter .

2.5.3 Capture variables

When Block captures variables, there is an extra * NSObject obj; in the structure for Block to capture.

Block essence

  1. In one sentence, Block is an object (its first internal member is the isa pointer)
  2. Why was Block born on the stack?
  • In its initialization function Please add image description
    impl.isa = &_NSConcreteStackBlock;
    _NSConcreteStackBlock is equivalent to the parent class of the block instance . When calling Block as an OC object, information about the class is placed in _NSConcretestackBlock, which also proves that the block is born on the stack. superior

2.6 Block captures variables and objects

variable

  • Global variables: do not capture
  • Local variables: capture values
  • Static global variables: do not capture
  • Static local variables: capture pointer
  • const modified local constant: capture value
  • Const-modified static local constants: capturing pointers

Object
BLOCK can capture objects, of which two methods need to be known.

When capturing the object, the code appears _main_block_copy_0and _main_block_depose_0.

  • __main_block_copy_0The function is to call _Block_object_assign, which is equivalent to retainassigning the object to the structure variable of the object type __main_block_impl_0. It is called when the Block on the stack is copied to the heap.
  • __main_block_dispose_0Calling _Block_object_disposeis equivalent releaseto releasing the object assigned to the structure variable of the object type. Will be called when the Block on the heap is discarded.
    Please add image description

2.7 Block memory management

Block memory analysis

When using block, heap block should pay attention to the scope problem. Weak reference pointer assignment will be released when it goes out of scope. This can be solved by strong reference.

3 Summary of Blcok’s Problems

1. Block is modifying NSMutableArray. Do I need to add __block?

int main(int argc, const char * argv[]) {
    
    
    @autoreleasepool {
    
    
        NSMutableArray *array = [NSMutableArray array];
        Block block = ^{
    
    
            [array addObject: @"20"];
            [array addObject: @"30"];
            NSLog(@"%@",array);
        };
        block();
    }
    return 0;

It can be executed correctly because blockthe memory address of array is only used in the block, and content is added to the memory address without modifying the memory address of arry. Therefore, array can be compiled correctly without using __block modification.

️So when you only use the memory address of local variables instead of modifying them, try not to add __block. Through the above analysis, we know that once the __block modifier is added, the system will automatically create the corresponding structure, occupying unnecessary space. memory space.

2. How does __block modify the internal value?

Block can capture values, see the following code
Please add image description

Please add image description

Principle: First, the age variable modified by __block is declared into a __Block_byref_age_0 structure named age. That is to say, if the __block modification is added, the captured variables in the block will be a structure of type __Block_byref_age_0

__Block_byref_age_0 structure

  • __isa pointer: There is also an isa pointer in __Block_byref_age_0, which means that __Block_byref_age_0 is essentially an object.
  • * __forwarding: __forwarding is of the __Block_byref_age_0 structure type, and the value stored in __forwarding is (__Block_byref_age_0 )&age, which is the memory address of the structure itself.
  • __flags :0
  • __size: sizeof(__Block_byref_age_0) is the memory space occupied by __Block_byref_age_0.
  • age: Where the variables are actually stored, local variables 10 are stored here.

Then store the __Block_byref_age_0 structure age into the __main_block_impl_0 structure, and assign it to __Block_byref_age_0 *age;
then call block, first take out the age in __main_block_impl_0, and get the __forwarding pointer through the age structure. What is saved in __forwarding is The __Block_byref_age_0 structure itself, here is age(__Block_byref_age_0), gets the age(10) variable in the structure through __forwarding and modifies its value.

__forwarding is a pointer to itself.
Please add image description

Summary: Why __block can modify the value of a variable is because __block packages the variable into an object with a pointer, and then encapsulates age in the structure. The variables stored inside the block are structure pointers, and the memory can also be found through the pointer. The address modifies the value of the variable.

3. Summary of __block and Block

Block is essentially an object, implemented using a structure.

//Block结构体
struct __main_block_impl_0 {
    
    
  struct __block_impl impl;//impl:Block的实际函数指针,就是指向包含Block主体部分的__main_block_func_0结构体
  struct __main_block_desc_0* Desc;//Desc指针,指向包含Block附加信息的__main_block_desc_0()结构体
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    
    //__main_block_impl_0:Block构造函数(可以看到都是对上方两个成员变量的赋值操作)
    impl.isa = &_NSConcreteStackBlock; // 
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__block function: You can get the pointer of the corresponding variable so that it can be modified inside the block.

The data structure of __block is as follows

struct __Block_byref_a_0 {
    
    
  void *__isa;//有isa,是一个对象
__Block_byref_a_0 *__forwarding;//指向自身类型对象的指针
 int __flags;//不用关心
 int __size;//自己所占大小
 int a;//被封装的 基本数据类型变量
};

4. Why is Block modified with copy?

Because the memory address of the Block is displayed in the stack area, the characteristic of the stack area is that the created object is destroyed at any time. Once destroyed, subsequent calls to the empty object will cause the program to crash.
After the copy operation is performed on the Block, the block exists in the heap area, so the Copy modification is used when using the Block attribute.

The block in the heap is the block modified by copy. Its life cycle ends with the destruction of the object. As long as the object is not destroyed, we can call the block in the heap.

This is why we use copy to modify the block. Because the block that accesses external variables without copy modification can only be used at the moment the function in which it is located is called. Then it disappeared.

Note: ARC Blocks are generally on the heap, because the system defaults to heap Blocks for copy operations. Regardless of ARC or MRC, modifying Block with copy Strong is a stack of Blocks.

5. What is the principle of block capturing variables?

  • When the grammar is executed Block, Blockthe value of the automatic variable used in the grammar expression is saved into Blockthe structure instance, that is, the Block itself.
  • One point worth explaining here is that if there are many automatic variables, static variables, etc. outside the Block, these variables will not be used inside the Block. Then these variables will not be captured by Block, that is to say, their values ​​will not be passed in the constructor.
  • Block captures external variables only to capture the values ​​that will be used in the Block closure. It will not capture other unused values.
    Insert image description here

Guess you like

Origin blog.csdn.net/weixin_61639290/article/details/131802720
Recommended