Application, circular reference and bottom layer of block

Time:2022-5-3
1、 Block syntax
Return type (^ block name) (parameter type) = ^ return type (variable type variable name) {implementation}

When a block is defined directly, the return type at the time of definition can be omitted, i.e

Return type (^ block name) (parameter type) = ^ (variable type variable name) {implementation}

If the parameter type is void, it can be omitted to write as

Return type (^ block name) (void) = ^ {implementation}

Anonymous block: when a block is defined, the one to the right of the equal sign is an anonymous block

1.2 declaration of typedef simplified block

Typedef return type (^ block name) (parameter type);

1.3 block is an object

NSLog(@"%@",block);

Output this block through this%@We can see that block is actually an object, which outputs the result__NSGlobalBlock__It can be seen that the block is stored in the global area

2、 Circular application of block

Three methods to solve the circular reference of block
1、__ Weak to solve

    self.name = @"hello";
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",strongSelf.name);
        });
    };
    
    block();

PS: in fact, many times,__strongWill be omitted, so that under certain circumstances, data loss will occur.
For example, we enter a page, execute the code block, and then exit the page in less than three seconds. If not__strongTo modify the member variables after the secondary page is destroyednameIt is also destroyed and released. In this thread executed after three seconds,nameThis member variable, throughgetterMethod, the return result will benull

2、__block

self.name = @"hello";
    __block ViewController *vc = self;

    self.block = ^{
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",vc.name);
            vc = nil; //  Self - > block - > VC (NIL) - > (break circular reference) block
        });
    };
    
    block();

3. It is solved through the block parameter

    self.name = @"hello";
    self.block = ^(ViewController *vc){
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@",vc.name);
        });
    };
    
    self.block(self);
3、 Classification
  • Stack block
    _NSConcreteStackBlock: the block saved in the stack will be destroyed when the function returns.

  • Heap block
    _NSConcreteMallocBlock
    Blocks saved in the heap will be destroyed when the reference count is 0.

  • Global block
    _NSConcreteGlobalBlock: global static block. External global variables and static variables can be accessed without accessing external local variables in the block. This is nsglobalblock.

3、 Code + bottom analysis
  • Check how the compiler implements block through the clang command, and enter clang – rewrite objc main m. Then it will generate main in the current directory CPP C + + files, but errors may be reported in this step, as follows:
In file included from /Users/apple/Desktop/Block_test/Block_test/ViewController.m:8:
/Users/apple/Desktop/Block_test/Block_test/ViewController.h:8:9: fatal error: 
      'UIKit/UIKit.h' file not found
#import <UIKit/UIKit.h>
        ^~~~~~~~~~~~~~~
1 error generated.
  • It doesn’t matter. You can try this method. It still works. CD it to the current file directory, and then replace the previously executed command with:
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode. app/Contents/Developer/Platforms/iPhoneSimulator. platform/Developer/SDKs/iPhoneSimulator. sdk  XXX. M file

success:

Application, circular reference and bottom layer of block

Screenshot 2021-04-26 5.25.49 PM png

1、Demo1

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];    

    void(^block)(void) = ^{        

        NSLog(@"hello world!");        

    };
    block();    

    // Do any additional setup after loading the view.
}
@end
  • Implementation of underlying code:
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

static struct __ViewController__viewDidLoad_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};

struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  //Constructor
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_db_80twdv6s18s_zk2z35wwh1cw0000gn_T_ViewController_8cc697_mi_0);
}

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

    void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
   
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);


}
  • To make it easier for us to read the code, let’s familiarize ourselves with several rules:
    ① The names of structures and methods in C + + can generally be regarded as splicing layer by layer
    ②、xxx_ 0 means that this 0 represents the first, that is, the first
    ③ There are many strong conversions in C + +, so you can delete a certain amount of code.

  • code analysis
    Through the bottom layer, we can see that the C + + implementation of block is a structure with two member variables(impl* Desc)And a constructor (a function that initializes the structure).

Next, let’s look at his two member variables:

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

__block_implIt is also a structure

*isa: isa pointer, pointing to a class object, has three types:_ NSConcreteStackBlock、_ NSConcreteGlobalBlock、_ NSConcreteMallocBlock。
Flags: load information (reference count and type information) of block, stored in bits.
Reserved: keep variables.
*FuncPtr: a pointer to the function called when the block is executed, that is, the code block to be executed by the block. In this case, yes__ViewController__viewDidLoad_block_func_0Function.

__ViewController__viewDidLoad_block_desc_0
static struct __ViewController__viewDidLoad_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};

__ViewController__viewDidLoad_block_desc_0Is a structure that contains two member variables:

Reserved: reserved space required for block version upgrade. Here, it is 0.
Block_ Size: block size (sizeof (struct _blocktest _block_impl_0)).
__ViewController__viewDidLoad_block_desc_0_DATAIt’s a__ViewController__viewDidLoad_block_desc_0An example of.

__ViewController__viewDidLoad_block_func_0

__ViewController__viewDidLoad_block_func_0It is the function called during the execution of block. The parameter is a__ViewController__viewDidLoad_block_impl_0Pointer to type.

static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_db_80twdv6s18s_zk2z35wwh1cw0000gn_T_ViewController_8cc697_mi_0);
}
Viewdidload method
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {

    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

    void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
   
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

Code simplification and one-to-one correspondence

static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    
    // [super viewDidLoad];
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    // void(^block)(void) = ^{NSLog(@"hello world");};
    void(*block)(void) = &__ViewController__viewDidLoad_block_impl_0
                         (
                           __ViewController__viewDidLoad_block_func_0,
                           &__ViewController__viewDidLoad_block_desc_0_DATA
                         );
    // block();
    block->FuncPtr(block);
}
  • The second line of code: defines the block.

We see that the block becomes a pointer to a pass__ViewController__viewDidLoad_block_impl_0Constructor instantiated structure__ViewController__viewDidLoad_block_impl_0example,__ViewController__viewDidLoad_block_impl_0Two parameters are required during initialization:

__ViewController__viewDidLoad_block_func_0: function pointer to the block.
__ViewController__viewDidLoad_block_desc_0_DATA: initialize as a static global variable__ViewController__viewDidLoad_block_desc_0Structure instance pointer for

  • The third line of code: block is called
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)adoptblock->FuncPtrPointer found__blockTest_block_func_0Function and convert to(void (*)(__block_impl *))Type.
    ((__block_impl *)block)Then pass the block as a parameter to the function call.
    Simply put, block calls XXXX_ func_ 0 method and passed itself.
Add: flags,__block_implPurpose of parameters

hereBlock_private.hCan seeFlagsSpecific information:

// Values for Block_layout->flags to describe block objects
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    BLOCK_IS_GC =             (1 << 27), // runtime
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};

quoteOn the rewritten block structure of block (1) – clangInterpretation of:

That is, in general, ablockThe flags member of is set to 0 by default. If whenblockneedBlock_copy()andBlock_releaseThis kind of copy auxiliary function will be set to1 << 25, that isBLOCK_HAS_COPY_DISPOSEType. You can search a lotBlock_copyMethod blog, which involvesBLOCK_HAS_COPY_DISPOSE

To summarize the usage of enumeration classes, the first 16 bits not only serve as markers, but also record reference counts:

  • BLOCK_DEALLOCATING: release the tag. Commonly used block_ NEEDS_ Free performs bit and operation, and passes in flags together to tell the block that it can be released.
  • BLOCK_REFCOUNT_MASK: generally involved in judging reference count. It is an optional parameter.
  • BLOCK_NEEDS_FREE: set the enumeration bit to tell the block that it can be released. It is intended to explain that a block is a heap block, which we often say_ NSConcreteMallocBlock 。
  • BLOCK_HAS_COPY_DISPOSE: whether there is a copy helper function.
  • BLOCK_HAS_CTOR: whether it has a block destructor (dispose function).
  • BLOCK_IS_GC: whether to enable GC mechanism (garbage collection).
  • BLOCK_HAS_SIGNATURE: and block_ USE_ Relative to string, judge whether the current block has a signature. Used for dynamic invocation at runtime.
Demo1 summary

The above is the bottom implementation of the most basic block. In fact, we have only taken the first step here. You want when we use__ block 、__ What happens when weak and block call external variables? How is the bottom layer realized? How are variables captured?

Demo2 probe block intercepts variables
Intercept auto variable value
Application, circular reference and bottom layer of block

image.png

We see that modifying variables directly in the block will prompt an error. Why?

void blockTest()
{
    int num = 10;
    void (^block)(void) = ^{
        NSLog(@"%d",num);
    };
    num = 20;
    block();
}

int main(int argc, char * argv[]) {
    @autoreleasepool {
        blockTest();
    }
}

The print result is 10. The rewritten code of Lang is as follows:

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

static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
  int num = __cself->num; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_3c2714_mi_0,num);
    }
    
    void blockTest()
{
    int num = 10;
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, num));
    num = 20;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

__blockTest_block_impl_0One more member variableint num;, look at the constructor__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int _num, int flags=0), you can see that the third parameter is only the value of the variable, which explains why 10 is printed because the block intercepts the value. The pointer was not intercepted.

Modify variables with static
void blockTest()
{
    static int num = 10;
    void (^block)(void) = ^{
        NSLog(@"%d",num);
        num = 30;
    };
    num = 20;
    block();
    NSLog(@"%d",num);
}

The internal variable is 20, and the result can be printed at the same time. The rewritten code of clang is as follows:

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

static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
  int *num = __cself->num; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_0,(*num));
        (*num) = 30;
    }
    
    void blockTest()
{
    static int num = 10;
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, &num));
    num = 20;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_1,num);
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

__blockTest_block_impl_0One more member variableint *num;, different from the above, the block interceptsPointerTherefore, the value of the variable can be modified internally through the pointer, and the value of the variable can be modified externally, and the block can also “perceive”. So why didn’t you pass the pointer before? Because the variable is on the stack and the scope is within the function blocktest, it is possible that the variable is destroyed before the block. At this time, there will be a problem if the block accesses the variable through the pointer. The static modified variable will not be destroyed, so you don’t have to worry.

global variable
int num = 10;

void blockTest()
{
    void (^block)(void) = ^{
        NSLog(@"%d",num);
        num = 30;
    };
    num = 20;
    block();
    NSLog(@"%d",num);
}

The print result is 20, 30. The rewritten code of clang is as follows:

int num = 10;


struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_1875c6_mi_0,num);
        num = 30;
    }

Very simple, in initialization__blockTest_block_impl_0It doesn’t take num as a parameter,__blockTest_block_func_0It is also a direct access to global variables.

Summary:
Variable type Capture inside the block Access mode
Local auto variable yes pass by value
Local static variable yes Pointer passing
global variable no Direct access
Use__ Block modifier variable
void blockTest()
{
    __block int num = 10;
    void (^block)(void) = ^{
        NSLog(@"%d",num);
        num = 30;
    };
    num = 20;
    block();
    NSLog(@"%d",num);
}

The effect is the same as using static to modify variables. The rewritten code of Lang is as follows:

struct __Block_byref_num_0 {
  void *__isa;
__Block_byref_num_0 *__forwarding;
 int __flags;
 int __size;
 int num;
};

struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  __Block_byref_num_0 *num; // by ref
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, __Block_byref_num_0 *_num, int flags=0) : num(_num->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
  __Block_byref_num_0 *num = __cself->num; // bound by ref

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_0,(num->__forwarding->num));
        (num->__forwarding->num) = 30;
    }
    
static void __blockTest_block_copy_0(struct __blockTest_block_impl_0*dst, struct __blockTest_block_impl_0*src) {_Block_object_assign((void*)&dst->num, (void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __blockTest_block_dispose_0(struct __blockTest_block_impl_0*src) {_Block_object_dispose((void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __blockTest_block_impl_0*, struct __blockTest_block_impl_0*);
  void (*dispose)(struct __blockTest_block_impl_0*);
} __blockTest_block_desc_0_DATA = { 0, sizeof(struct __blockTest_block_impl_0), __blockTest_block_copy_0, __blockTest_block_dispose_0};

void blockTest()
{
    __attribute__((__blocks__(byref))) __Block_byref_num_0 num = {(void*)0,(__Block_byref_num_0 *)&num, 0, sizeof(__Block_byref_num_0), 10};
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, (__Block_byref_num_0 *)&num, 570425344));
    (num.__forwarding->num) = 20;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_1,(num.__forwarding->num));
}

Wow, it’s hard, brother Dei. How can so many things come out? It doesn’t matter. Analyze it slowly.

__blockTest_block_impl_0One more member variable__Block_byref_num_0 *num;, we saw the passing__blockThe modified variable type becomes a structure__Block_byref_num_0__blockTest_block_impl_0One more member variable__Block_byref_num_0 *num;, block captures__Block_byref_num_0Type pointer,

__Block_byref_num_0
We see__Block_byref_num_0Is a structure and has aisaTherefore, we can know that it is actually an object. There is also one__Block_byref_a_0 *Type__forwardingandnumnumWe can guess that it is used to save the value of the variable,__forwardingIt’s a little complicated. I’ll talk about it later.

__blockTest_block_copy_0and__blockTest_block_dispose_0

__blockTest_block_copy_0Is called in_Block_object_assign__blockTest_block_dispose_0Is called in_Block_object_dispose

function Call timing
__blockTest_block_copy_0 __ When the block variable structure instance is copied from the stack to the heap
__blockTest_block_dispose_0 __ When the block variable structure instance reference count is 0

about_Block_object_assignand_Block_object_disposeMore detailed codes can be found inruntime.c View in.

// Runtime support functions used by compiler when generating copy/dispose helpers

// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
    // see function implementation for a more complete description of these fields and combinations
    BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable
    BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable
    BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpers
    BLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.
};
  • BLOCK_FIELD_IS_OBJECT: OC object type
  • BLOCK_FIELD_IS_BLOCK: it’s a block
  • BLOCK_FIELD_IS_BYREF: on the stack__ Variable modified by block
  • BLOCK_FIELD_IS_WEAK: be__ The variable modified by weak is only in block_ Byref is used when managing internal object memory
  • BLOCK_BYREF_CALLER: process block_ Byref is an extra mark that will be added when the internal object is stored in memory (tell the internal implementation not to retain or copy)

__blockTest_block_desc_0
We can see that it has two more callback function pointers*copyand*dispose, these two pointers will be assigned__main_block_copy_0and__main_block_dispose_0

Finally, we see that accessing num is as follows:

__Block_byref_num_0 *num = __cself->num; // bound by ref   

(num->__forwarding->num) = 30;

Let’s talk about why.

Memory management of block

As we mentioned earlier__block_impldirective_NSConcreteStackBlockThere are actually three types of class objects of type:

type Storage area
_NSConcreteStackBlock Stack
_NSConcreteGlobalBlock Data area
_NSConcreteMallocBlock heap

As mentioned earliercopyanddispose, in arc environment, under what circumstances will the compiler automatically copy the block on the stack from the stack to the heap?

Copy block from stack to heap
When calling copy instance method of block
When block is returned as the return value of the function
When passing a block in the cocoa method with usingblock or the API of GCD
Assign block to__ StrongWhen the ID type or block type of the modifier

WhenBockCopy from stack to heap,__blockIt also changes:

Application, circular reference and bottom layer of block

image.png

WhenBlockOn the stack,__blockThe storage domain of is stack,__blockThe variable is on the stackBlockHold.
WhenBlockWhen copied to the heap, theBlockInternalcopyFunction,copyThe function will be called internally_Block_object_assignFunction. here__blockThe storage domain of variables is heap,__The block variable is on the heapBlockHold.
When on the pileBlockIs released, will callBlockInternaldisposedisposeThe function will be called internally_Block_object_dispose, on the pile__blockReleased.

Application, circular reference and bottom layer of block

image.png
  • When on multiple stacksBlockUsing on stack__blockVariables,__blockVariables are used by multiple on the stackBlockHold.
  • WhenBlock0When copied to the heap,__blockIt will also be copied to the heapBlock0Hold.Block1Still holding on to the stack__block, on the original stack__blockVariable__forwardingRefers to the data after copying to the heap__blockVariable.
  • WhenBlock1Also copied to the heap when the__blockPiled upBlock0andBlock1Only, and__blockReference count of + 1.
  • When on the pileBlockWere released,__blockVariable structure instance reference count is 0, call_Block_object_dispose, on the pile__ Block is released.

The following figure is a description__forwardingChange. That explains__forwardingMeaning of existence:

__ Forwarding ensures that the corresponding variables can be accessed correctly on the stack or heap

Application, circular reference and bottom layer of block

image.png
int main(int argc, char * argv[]) {

    int num = 10;

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

    void (^block)(void) = ^{
        NSLog(@"%d",num);
    };

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

Print results:

2019-05-04 18:40:48.470228+0800 BlockTest[35824:16939613] __NSStackBlock__
2019-05-04 18:40:48.470912+0800 BlockTest[35824:16939613] __NSMallocBlock__
  • We can see the first oneBlockNot assigned to__strongPointer, and the secondBlockAssign to__strongPointer, so the first one is on the stack and the second one is on the heap.
Block intercept object (auto variable of object type)

1. Define a simple block:

  MJBlock block;
  {
    MJPerson *person = [[MJPerson alloc] init];
    person.age = 10;
    block = ^{
      NSLog(@"---------%d", person.age);
    };
  }
  NSLog(@"------");
  • After the second nslog is printed, the person will not be destroyed, because the block has a pointer to the external person object. The block is on the heap and is of malloc type. If the block is not destroyed, the person will not be destroyed. However, if the MRC environment is changed, the block on the stack will not strongly reference the auto object. But if person uses__ If weak is modified, person will be destroyed first.
    Change the above block to the following:
        MJBlock block;
        {
            MJPerson *person = [[MJPerson alloc] init];
            person.age = 10;
            __weak MJPerson *weakPerson = person;
            block = ^{
                NSLog(@"---------%d", weakPerson.age);
            };
        }
        NSLog(@"------");
  • In this case, use the previous oneclang xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.mErrors will be reported when converting to the underlying C + + codecannot create __weak reference in file using manual referenceBecause weak is a weak reference and is made at runtime, it is usedxcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.mAfter conversion:
typedef void (*MJBlock)(void);
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  MJPerson *__weak person;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person(_person) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  MJPerson *__strong person = __cself->person; // bound by copy
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_c41e64_mi_0, ((int (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("age")));
            }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

Previous mjpherson * person; Also converted to mjperson*__ weak person;

The summary is as follows:

  • When the auto variable of the object type is accessed inside the block
  • 1. If the block is on the stack, neither the strong pointer nor the weak pointer will make a strong reference to the auto object.
  • 2. If the block is copied to the heap. In the arc environment, when a block is strongly referenced, the copy operation will be carried out. If the block is copied, the internal function will be called_ Block_ object_ Assign function. Calling this function will modify the keyword mjperson according to the external auto object*__ Weak person or mjperson*__ Strong person refers to auto strongly or weakly
  • 3. If the block is removed from the heap. The dispose function inside the block will be called, and the dispose function will be called internally_ Block_ object_ Dispose function_ Block_ object_ The dispose function automatically releases the referenced auto variable, similar to the release operation.

    Application, circular reference and bottom layer of block

    image.png

Interview questions:

    MJPerson *p = [[MJPerson alloc] init];
    __weak MJPerson *weakP = p;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1-------%@", p);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"2-------%@", weakP);
        });
    });
    NSLog(@"-----------");

After the transformation of the positions of nslog1 and nslog2, it is concluded that the release time of person depends on when the strong reference of person is released, regardless of the release time of weak reference

There are some differences between the declared references of block in arc and MRC
  • Blocks can be stored on the stack or in the heap
  • By default, it is stored in the stack, and there is no need to manage memory
  • The block stored in the heap will retain the block
  • (MRC) when a block is in the heap, you do not want to perform a retain operation on the block, preceded by__ block
  • (ARC) preceded by__ Weak or__ unsafe_ unretained
  • __ Weak and__ unsafe_ The difference between unretained:__ Weak will assign nil to the object when it is released, and the latter will not
  • Block_ Copy transfers the block in the stack to the heap and performs a retain operation on the object that the block will reference
  • Avoid the retain operation on the object referenced by the block, and precede the declaration of the referenced object__ block
Get reference count under arc (retain count)

Note: the following methods can only be used for debugging, and the return value is not 100% trusted in the case of multithreading.

1. Use KVC

[obj valueForKey:@"retainCount"]

2. Use private API

OBJC_EXTERN int _objc_rootRetainCount(id);
_objc_rootRetainCount(obj)

3. Use cfgetretaincount

CFGetRetainCount((__bridge CFTypeRef)(obj))

source
Link:https://www.jianshu.com/p/221d0778dcaa
Link:https://www.jianshu.com/p/60c0bc161201
Link:https://blog.csdn.net/weixin_37547351/article/details/105106559
Link:https://blog.csdn.net/zhangwenhai001/article/details/46702271
Link:https://blog.csdn.net/iuyo89007/article/details/51761720/