⑦ Block related interview questions

Time:2021-12-1

1、 What is block?

A block is an object that encapsulates a function and its execution context.

For example:

NSInteger num = 3;
    
    NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
        
        return n*num;
    };
    
    block(2);

Compile the. M file with the command Lang – rewrite objc wytest. M and find that the block is compiled in this form:

    NSInteger num = 3;

    NSInteger(*block)(NSInteger) = ((NSInteger (*)(NSInteger))&__WYTest__blockTest_block_impl_0((void *)__WYTest__blockTest_block_func_0, &__WYTest__blockTest_block_desc_0_DATA, num));

    ((NSInteger (*)(__block_impl *, NSInteger))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 2);

Where wytest is the file name and blocktest is the method name, which can be ignored.
Among them__ WYTest__ blockTest_ block_ impl_ 0 structure is

struct __WYTest__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __WYTest__blockTest_block_desc_0* Desc;
  NSInteger num;
  __WYTest__blockTest_block_impl_0(void *fp, struct __WYTest__blockTest_block_desc_0 *desc, NSInteger _num, int flags=0) : num(_num) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

__ block_ Impl structure is

struct __block_impl {
  void *isa;// Isa pointer, so block is an object
  int Flags;
  int Reserved;
  void *FuncPtr;// Function pointer
};

There is an ISA pointer inside the block, so it is also an OC object in essence
Inside the block:

static NSInteger __WYTest__blockTest_block_func_0(struct __WYTest__blockTest_block_impl_0 *__cself, NSInteger n) {
  NSInteger num = __cself->num; // bound by copy


        return n*num;
    }

Therefore, a block is an object that encapsulates a function and its execution context
Since block encapsulates functions internally, it also has parameters and return values.

2、 Block variable interception

1. Local variable interception is value interception. For example:

    NSInteger num = 3;
    
    NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
        
        return n*num;
    };
    
    num = 1;
    
    NSLog(@"%zd",block(2));

The output here is 6 instead of 2, because the interception of the local variable num is value interception.
Similarly, if you modify the variable num in the block, it is also invalid, and even the compiler will report an error.

NSMutableArray * arr = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
    
    void(^block)(void) = ^{
        
        NSLog(@"%@",arr);// local variable
        
        [arr addObject:@"4"];
    };
    
    [arr addObject:@"3"];
    
    arr = nil;
    
    block();

Print as 1, 2, 3
The same is true for local object variables. The intercepted value, rather than the pointer, is set to nil externally, which has no impact on the block, and the method call of the object will affect the block

2. Local static variable interception is pointer interception.

   static  NSInteger num = 3;
    
    NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
        
        return n*num;
    };
    
    num = 1;
    
    NSLog(@"%zd",block(2));

The output is 2, which means num = 1. The modified num value here is valid, that is, pointer interception.
Similarly, it is also effective to modify the variable m in the block.

3. Global variable, static global variable interception: no interception, direct value.

Let’s also compile with clang and see the results.

static NSInteger num3 = 300;

NSInteger num4 = 3000;

- (void)blockTest
{
    NSInteger num = 30;
    
    static NSInteger num2 = 3;
    
    __block NSInteger num5 = 30000;
    
    void(^block)(void) = ^{
        
        NSLog(@"%zd",num);// local variable
        
        NSLog(@"%zd",num2);// Static variable
        
        NSLog(@"%zd",num3);// Global static variable
        
        NSLog(@"%zd",num4);// global variable
        
        NSLog(@"%zd",num5);//__ Block modifier variable
    };
    
    block();
}

After compilation

struct __WYTest__blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __WYTest__blockTest_block_desc_0* Desc;
  NSInteger num;// local variable
  NSInteger *num2;// Static variable
  __ Block_ byref_ num5_ 0 *num5; //  by ref//__ Block modifier variable
  __WYTest__blockTest_block_impl_0(void *fp, struct __WYTest__blockTest_block_desc_0 *desc, NSInteger _num, NSInteger *_num2, __Block_byref_num5_0 *_num5, int flags=0) : num(_num), num2(_num2), num5(_num5->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

(impl. Isa = &_nsconcretestackblock; if you notice this sentence here, it means that the block is a stack block)
You can see that local variables are compiled into value form, while static variables are compiled into pointer form, and global variables are not intercepted. And__ The variable modified by block is also intercepted in the form of pointer, and a new structure object is generated:

struct __Block_byref_num5_0 {
  void *__isa;
__Block_byref_num5_0 *__forwarding;
 int __flags;
 int __size;
 NSInteger num5;
};

The object has an attribute: num5, which we use__ Variable modified by block.
Here__ Forwarding is a stack block that points to itself.
In general, if we want to assign values to the local variables intercepted by block, we need to add__ block
Modifier, while for global variables, static variables do not need to be added__ Of the block modifier.
In addition, accessing self or member variables in the block will intercept self.

3、 Several forms of block

  • It is divided into three forms: Global block (_nsconcreteglobalblock), stack block (_nsconcretestackblock) and heap block (_nsconcretemallocblock)
    Stack blocks are stored in the stack area, heap blocks are stored in the heap area, and global blocks are stored in the initialized data (. Data) area

1. A block that does not use external variables is a global block

For example:

    NSLog(@"%@",[^{
        NSLog(@"globalBlock");
    } class]);

Output:

__NSGlobalBlock__

2. A block that uses an external variable and does not copy is a stack block

For example:

  NSInteger num = 10;
    NSLog(@"%@",[^{
        NSLog(@"stackBlock:%zd",num);
    } class]);

Output:

__NSStackBlock__

Daily development is often used in this case:

[self testWithBlock:^{
    NSLog(@"%@",self);
}];

- (void)testWithBlock:(dispatch_block_t)block {
    block();

    NSLog(@"%@",[block class]);
}

3. Copying a stack block is a heap block, while copying a global block is still a global block

  • For example, copy operation is performed on the global in heap 1, that is, assignment:
void (^globalBlock)(void) = ^{
        NSLog(@"globalBlock");
    };

 NSLog(@"%@",[globalBlock class]);

Output:

__NSGlobalBlock__

Global block

  • The stack block in 2 is assigned:
NSInteger num = 10;

void (^mallocBlock)(void) = ^{

        NSLog(@"stackBlock:%zd",num);
    };

NSLog(@"%@",[mallocBlock class]);

Output:

__NSMallocBlock__

Copying a stack block does not mean that the stack block disappears. The mallock on the left is a heap block, and the stack block copied on the right is still a stack block
For example:

[self testWithBlock:^{
    
    NSLog(@"%@",self);
}];

- (void)testWithBlock:(dispatch_block_t)block
{
    block();
    
    dispatch_block_t tempBlock = block;
    
    NSLog(@"%@,%@",[block class],[tempBlock class]);
}

Output:

__NSStackBlock__,__NSMallocBlock__
  • That is, if the stack block is copied, it will be copied to the heap area. If the heap block is copied, the reference count will be increased and the global block will be copied. Because it has been initialized, nothing will be done.

In addition__ When the block variable is in copy, because__ The existence of forwarding, on the stack__ The forwarding pointer points to the on the heap__ Forwarding variable, and on the heap__ The forwarding pointer points to itself, so if__ The modification of block is actually on the modification heap__ Block variable.

Namely__ The meaning of the forwarding pointer is that the same memory can be accessed smoothly at any memory location__ Block variable

  • In addition, due to the data captured by block__ The variable modified by block will hold the variable, so if you use__ Block modifies self, and self holds the block, and the block is used internally__ When block modifies self, it will cause multiple circular references, that is, self holds block and block holds__ Block variable, and__ The block variable holds self, causing a memory leak.
    For example:
 __block typeof(self) weakSelf = self;
    
    _testBlock = ^{
        
        NSLog(@"%@",weakSelf);
    };
    
    _testBlock();

If you want to solve this circular reference, you can actively break it__ The block variable holds self, that is, after using weakself inside the block, it is set to nil. However, there is a problem with this method. If the block is not called all the time, the circular reference will always exist.
So we’d better use it__ Weak to modify self

Recommended Today

8 Vue component communication modes (Reprint)

This article takes you to learn more about the eight component communication modes in Vue. There is a certain reference value. Friends in need can refer to it. I hope it will be helpful to you. Vue is the framework of data-driven view update, so the data communication between components is very important for Vue, […]