Block Interception Automatic Variable Implementation and _block Modifier Internal Implementation

Keywords: Programming

meet Block Interception Automated Variable Implementation and _block Modifier Internal Implementation Let's continue to talk about Block

Questions left behind

  • 1, _Block_byref_i_0*_forwarding; what's the ghost of this pointer to itself, what's its function, and when to use it?
  • 2. What are the effects of the two methods of void (* copy) and void (* dispose) added to the Desc_0 structure? When will they be used?

The Essence of Block and _block Variables

Name essence
Block Structural Instance of Block on Stack
_ block variable Structural instance of _block variable on stack

Block Types

struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

We all know that the ISA pointer is a pointer to its class, so impl.isa= &NSConcreteStackBlock; that means that the block is of type _NSConcreteStackBlock. Block on the stack.

And of course

class Setting the object's storage domain
_NSConcreteStackBlock Stack
_NSConcreteGlobalBlock Data area of program (. data area)
_NSConcreteMallocBlock heap

Under what circumstances is a Block of type _NSConcreteGlobalBlock

According to the name, global (global) let's create a global Block and see its compilation results.

#import "ViewController.h"
//Global Block
void (^blk)(void) = ^{NSLog(@"123");};

typedef void(^WxsBlock) ();

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    blk();
}

@end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

After compilation

struct __blk_block_impl_0 {
  struct __block_impl impl;
  struct __blk_block_desc_0* Desc;
  __blk_block_impl_0(void *fp, struct __blk_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteGlobalBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __blk_block_func_0(struct __blk_block_impl_0 *__cself) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_t9_g3xrsv653kz2gr7tmgwfbfvh0000gn_T_ViewController_fbc035_mi_0);}

static struct __blk_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blk_block_desc_0_DATA = { 0, sizeof(struct __blk_block_impl_0)};

static __blk_block_impl_0 __global_blk_block_impl_0((void *)__blk_block_func_0, &__blk_block_desc_0_DATA);

void (*blk)(void) = ((void (*)())&__global_blk_block_impl_0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

We find impl. isa = & _NSConcreteGlobalBlock;

Let's look at the type of block as a local variable, function return value, and function parameter.

Interception with Automated Variables

@property(nonatomic, assign) blk_t blockAssgin;
@property(nonatomic, strong) blk_t blockStrong;
@property(nonatomic, copy) blk_t blockCopy;


blk_t getBlk_t(int i) {
    blk_t block4 = ^(int count){return count*i;};
    NSLog(@"As a function return value, there is automatic variable interception---%@",block4);
     return block4;
}


{
    //As a general variable, there is automatic variable interception
    blk_t block1 = ^(int count) {
        return count * value;
    };
    NSLog(@"As a general variable, there is automatic variable interception---%@",block1);


    //As a member variable of assign type, there is automatic variable interception
    self.blockAssgin = ^(int count) {
        return count * value;
    };
    NSLog(@"Act as assign Type member variables, with automatic variable interception---%@",self.blockAssgin);

    //As a member variable of strong type, there is automatic variable interception
    self.blockStrong = ^(int count) {
        return count * value;
    };
    NSLog(@"Act as strong Type member variables, with automatic variable interception---%@",self.blockStrong);


    //As a member variable of copy type, there is automatic variable interception
    self.blockCopy = ^(int count) {
        return count * value;
    };
    NSLog(@"Act as copy Type member variables, with automatic variable interception---%@",self.blockCopy);

    //As a function return value, there is automatic variable interception
    getBlk_t(value);

    //As a function parameter, there is automatic variable interception
    [self actionBlock:^int(int i) {
        return i*value;
    }];
}

- (void)actionBlock:(blk_t)block {
    //As a function parameter, there is no automatic variable interception
    NSLog(@"As a function parameter, there is automatic variable interception---%@",block);
    block(1);
}

NSLog Journal:

Block[47786:5956467] As a general variable, there is automatic variable interception---<__NSMallocBlock__: 0x60000024c9c0>
Block[47786:5956467] Act asassignType member variables, with automatic variable interception---<__NSStackBlock__: 0x7fff5fbc39e0>
Block[47786:5956467] Act asstrongType member variables, with automatic variable interception---<__NSMallocBlock__: 0x608000056110>
Block[47786:5956467] Act as copy Type member variables, with automatic variable interception---<__NSMallocBlock__: 0x608000056620>
Block[47786:5956467] As a function return value, there is automatic variable interception---<__NSMallocBlock__: 0x6080000565c0>
Block[47786:5956467] As a function parameter, there is automatic variable interception---<__NSStackBlock__: 0x7fff5fbc3968>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

But as a result of clang compilation, all of them are impl. isa = and _NSConcreteStackBlock. No surprise?

Interception without automatic variables

{
    //As a return value, there is no automatic variable interception
    blk_t blockt = getBlk_t();
    NSLog(@"As a return value, there is no automatic variable interception blockt---%@",blockt);

    //As a normal variable, there is no automatic variable interception. Here we try @property (strong, assgin, copy), which is the same result.
    self.blocktest = ^(int count){return count;};
    NSLog(@"As a general variable, there is no automatic variable interception----%@",self.blocktest);

    //As a function parameter, there is no automatic variable interception
    [self actionBlock:^int(int i) {
        NSLog(@"%d",i);
        return i;
    }];
}

- (void)actionBlock:(blk_t)block {
    //As a function parameter, there is no automatic variable interception
    NSLog(@"As a function parameter, there is no automatic variable interception---%@",block);
    block(1);
}

//Print results
Block[47535:5919554] As a return value, there is no automatic variable interception blockt---<__NSGlobalBlock__: 0x1039fd0f0>
Block[47535:5919554] As a general variable, there is no automatic variable interception----<__NSGlobalBlock__: 0x1039fd150>
Block[47535:5919554] As a function parameter, there is no automatic variable interception---<__NSGlobalBlock__: 0x1039fd190>


//All GlobalBlock
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

But as a result of clang compilation, all of them are impl. isa = and _NSConcreteStackBlock. No surprise?

By compiling clang, you can only see the type of Block in static state (static code declaration) under ARC. It feels like it can only be used as a reference, after all, there is runtime.

So we can summarize the results of NSLog printing briefly.

Block Types Is there automatic variable interception? Usage
_NSConcreteGlobalBlock Yes Declare and initialize as global variables
  nothing Overall information
     
NSStackBlock Yes Member variables of assgin type
    As a function parameter
  nothing nothing
     
_NSConcreteMallocBlock Yes As a general local variable
    As a member variable of type copy
    As a member variable of strong type
    Return value as function
  nothing nothing

Why is the _NSConcreteGlobal Block type set in the data area?

Because automatic variables are not intercepted where global variables are used. Because it was defined at the beginning. There is no "variable", so its execution is fixed at any time, and it does not depend on the running environment. This type of operation can only be operated globally, which saves space and manages well. So it's best to put it in the data area of the global static zone.

Summary of comparison between clang compilation and NSLog

From the result of clang compilation, except when the global variable is declared and created, the block is compiled into _NSConcreteGlobalBlock, which is the type of _NSConcreteMallocBlock all the time. It is not difficult to see that there are only two initial states of the block, so the block of Malloc state transits from the state to the past. A guess< Objective-C Advanced Programming coincides with the author.

So let's talk about how it got into the heap from the stack.

According to the description in Objective-C Advanced Programming:
It uses the objc_retainBlock() method to get the Block from the stack to the heap.
From the runtime/objc-arr.mm of the objc4 runtime:
objc_retainBlock() = _Block_copy

Why is a Block in the heap when it is returned as a function? What is it doing?

blk_t func(int rate) {

    /*
        Block grammar is used to generate Block and configure an instance of the structure on the stack.
        tmp Appointed
    */
    blk_t tmp = &__func_block_impl_0(...);

   /*
    Copy it to the heap through _Block_copy
    Copy the address on the heap as a pointer to the variable tmp
   */
    tmp = _Block_copy(tmp);

   /*
    Block on the heap as Objective-C object
    Register in autorelease pool and return the object
   */
    return objc_autoreleaseReturnValue(tmp);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Block replication in ARC is done by the compiler itself, but the compiler is not omnipotent.

When used as a return value, the compiler is clearly able to handle it

But as a function parameter, it is not so good. At this time, we need to copy the Block manually. This is verified by an example of "as a function parameter, there is automatic variable interception", which is stack type.

I'm sure you'll be asked, why do you have to copy to the heap? Not on the stack?

Two words: safe!  
The _Block and Block variables copied to the heap are not affected at the end of the variable scope

When need manual Copy

When a Block is retransmitted to a parameter of a method or function

But if the passed parameters are copied appropriately in a method or function, then it is not necessary to copy them manually before calling the method or function, such as the following method:

1. The method of Cocoa Framework and the method name has using Block and so on.
2. API of GCD

Posted by Entanio on Sat, 06 Jul 2019 15:45:26 -0700