Block portal πŸ¦‹πŸ¦‹πŸ¦‹

Exploring the nature of blocks (1) — Basic knowledge

Explore the essence of blocks (2) — the underlying structure

Explore the essence of blocks (3) — basic type of variable capture

Explore the nature of blocks (4) — the type of Block

Exploring the nature of blocks (6) — An in-depth analysis of __blocks

Let’s start with a code example

* * * * * * * * * * * * * * * * * * * * *CLPerson.h*********************
#import <Foundation/Foundation.h>

@interface CLPerson : NSObject
@property (nonatomic.assign) int age;
@end* * * * * * * * * * * * * * * * * * * * *CLPerson.m*********************
#import "CLPerson.h"

@implementation CLPerson- (void)dealloc {
    NSLog(@"%s",__func__);
}
@end

*********************main.m*********************
#import <Foundation/Foundation.h>
#import "CLPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool{{// The temporary scope starts
            CLPerson *person = [[CLPerson alloc] init];
            person.age = 10;
        }// The temporary scope ends
        
        NSLog(@"-----------flag1");
    }
    return 0;
}

Copy the code

By printing the markflag1Breakpoint debugging can be seen in the temporary scopepersonIt is easy to understand that objects are released as soon as they go out of scope.

Add block to the above code and adjust it as follows

* * * * * * * * * * * * * * * * * * * * *CLPerson.h*********************
#import <Foundation/Foundation.h>

@interface CLPerson : NSObject
@property (nonatomic.assign) int age;
@end* * * * * * * * * * * * * * * * * * * * *CLPerson.m*********************
#import "CLPerson.h"

@implementation CLPerson- (void)dealloc {
    NSLog(@"%s",__func__);
}
@end

*********************main.m*********************
#import <Foundation/Foundation.h>
#import "CLPerson.h"

typedef void(^CLBlock) (void);/ / βž• βž• βž•

int main(int argc, const char * argv[]) {
    @autoreleasepool {
	
		CLBlock myBlock;/ / βž• βž• βž•
        {// The temporary scope starts
            CLPerson *person = [[CLPerson alloc] init];
            person.age = 10;

			 myBlock = ^{/ / βž• βž• βž•
                NSLog(@"---------%d",person.age);
            };
        }// The temporary scope ends
        
        NSLog(@"-----------flag1");
    }
    return 0;
}
Copy the code

Print the tag againflag1At breakpoint debug runAnd it tells us that out of the temporary scope,personThe object was not released. There are two caveats:

  • Because now it’sARCThe environment,myBlockIs a strong pointer, therefore, in theblockObject assigned tomyBlockWhen pointer, the compiler will automaticallyblockObject to performcopyOperation, so when the assignment is complete,myBlockIt’s pointing at a heap spaceblockObject copy.

Iphoneos clang-arch arm64-rewrite-objc main.m -o main. CPP


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  CLPerson *person;

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, CLPerson *_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) {
  CLPerson *person = __cself->person; // bound by copy

                NSLog((NSString *)&__NSConstantStringImpl__var_folders_7__p19yp82j0xd2m_1k8fpr77z40000gn_T_main_2cca58_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
                              };



int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        CLBlock myBlock;

        {
            CLPerson * person = objc_msgSend(objc_msgSend(objc_getClass("CLPerson"), 
                                                          sel_registerName("alloc")
                                                          ), 
                                             sel_registerName("init")); objc_msgSend(person, sel_registerName("setAge:"), 
                         30
                         );


            myBlock = objc_msgSend(&__main_block_impl_0(__main_block_func_0, 
                                                        &__main_block_desc_0_DATA, 
                                                        person, 
                                                        570425344), 
                                   sel_registerName("copy")); }}return 0;
}
Copy the code

This is the result of the ARC environment –> block on the heap –> strong pointer CLPerson *person and the underlying implementation. We noticed that the __main_block_desc_0 structure had two more eggs in it when block captured the auto variable of the object type, instead of capturing the auto variable of the primitive type

  • A function pointercopy, that is,__main_block_copy_0()Internally called_Block_object_assign()
  • A function pointerdispose, that is,__main_block_dispose_0()Internally called_Block_object_dispose()

The other thing to notice here is,ARC δΈ‹CLPerson *personIs considered to be a strong pointer, equivalent to_strong CLPerson *person, and weak Pointers need to be expressed explicitly as__weak CLPerson *person. By terminal commandXcrun-sdk iphoneOS clang-arch arm64-rewrite-fobjc-arc-fobjc-Runtime =ios-9.0.0 main.m -o main.cppAs you can seeblockCaptured in thepersonPointer to the following

For comparison, let’s take a look at the following three scenarios:

  • ARC environment –> on the heapblock— > weak pointer__weak CLPerson *person
  • ARC environment –> on the stackblock— > strong pointerCLPerson *person
  • ARC environment –> on the stackblock— > weak pointer__weak CLPerson *person





ARC environment –> block on heap –> weak pointer __weak CLPerson *person

***********************main.m*************************
#import <Foundation/Foundation.h>
#import "CLPerson.h"

typedef void(^CLBlock)(void);


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        CLBlock myBlock;
        
        {// The temporary scope starts
            __weak CLPerson * person = [[CLPerson alloc] init];
            person.age = 30;
            
            myBlock = ^{
                NSLog(@"---------%d",person.age); }; }// The temporary scope ends

        NSLog(@"-- -- -- -- -- -- -- -- -- -- -- -- --");
        
    }
    
    NSLog(@"------main autoreleasepool end-------");
    
    return 0;
}

Copy the code

The underlying structure of a block is as follows Run results show the values on the heapblockUsing weak Pointers__weak CLPerson *personIt has no effectpersonThe life cycle of the object to which it points is released when it is out of the temporary scope.





ARC –> block on stack –> strong pointer CLPerson *person

***********************main.m*************************
#import <Foundation/Foundation.h>
#import "CLPerson.h"

typedef void(^CLBlock)(void);


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        CLBlock myBlock;
        
        {// The temporary scope starts
            CLPerson * person = [[CLPerson alloc] init];
            person.age = 30;
            
            ^{
                NSLog(@"---------%d",person.age); }; }// The temporary scope ends

        NSLog(@"-- -- -- -- -- -- -- -- -- -- -- -- --");
        
    }
    
    NSLog(@"------main autoreleasepool end-------");
    
    return 0;
}
Copy the code

The underlying structure of block is as follows

The result shows that the block on the stack uses a strong pointer CLPerson *person, which does not affect the life cycle of the object to which person points, and is freed when out of temporary scope.





ARC environment –> block on stack –> weak pointer __weak CLPerson *person

***********************main.m*************************
#import <Foundation/Foundation.h>
#import "CLPerson.h"

typedef void(^CLBlock)(void);


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        CLBlock myBlock;
        
        {// The temporary scope starts
            __weak CLPerson * person = [[CLPerson alloc] init];
            person.age = 30;
            
            ^{
                NSLog(@"---------%d",person.age); }; }// The temporary scope ends

        NSLog(@"-- -- -- -- -- -- -- -- -- -- -- -- --");
        
    }
    
    NSLog(@"------main autoreleasepool end-------");
    
    return 0;
}

Copy the code

The underlying structure of block is

The result shows that blocks on the stack use a weak pointer __weak CLPerson *person, which does not affect the life cycle of the object to which person points, and is released after the temporary scope.





If a block on the heap uses a strong pointer CLPerson *person, it will affect the lifetime of the object to which the pointer points.

__main_block_desc_0 function pointer copy and dispose. These two Pointers are actually used when a block is copied from stack space to heap space, and when a block in heap space is freed.

When a block is copied from the stack to the heap, the function __xxx_block_copy_x is called from the function pointer copy in the block

__xxx_block_copy_x(struct __main_block_impl_0*dst, 
				   struct __main_block_impl_0*src
				   );
Copy the code

Notice the two arguments to this function,

  • The first onedstBefore copyingBlocks in stack space.
  • The secondsrcAfter copyBlocks in heap space.

This function calls the _Block_object_assign function

_Block_object_assign((void*)&dst->person, 
					 (void*)src->person, 
					 3/*BLOCK_FIELD_IS_OBJECT*/
					 );
Copy the code

This function assigns the value of the Person pointer captured inside the DST to the Person pointer in the SRC stack block. In an ARC environment, SRC (heap block) holds objects based on the strength of person (the previous __weak and __strong modifiers). If person is decorated with __strong, the SRC (heap block) holds (strong reference) the object to which person points by making the reference count of the object to which person points +1 via [Person retain].

Dispose calls the function __xxx_block_dispose_x through its function pointer dispose when a block on the heap is about to be released

__main_block_dispose_0(struct __main_block_impl_0*src);
Copy the code

SRC is the block on the heap, which calls the _Block_object_dispose function internally

_Block_object_dispose((void*)src->person, 
					  3/*BLOCK_FIELD_IS_OBJECT*/
					  );
Copy the code

In ARC, this function determines what to do with the object to which person points by taking into account the __weak/__strong nature of the person pointer inside the SRC block. If __strong is used, SRC holds the object to which person points (a strong reference). Since SRC is about to be released, it needs to be released, i.e. call [Person Release] so that the reference count of the object to which person refers is -1.

Why do I emphasize ARC environment above, because MRC does not have __weak and __strong. ARC automatically does some memory management based on the __weak/__strong flags in our code. Believe the above analysis should be able to let you understand.

If you go back to MRC, the essence of memory management remains the same, just retain and release operations, and I personally think the process will be simpler than ARC. First, a block that internally uses the auto variable (base type/object type) is in stack space and does not retain the auto variable of the object type, i.e. does not hold the object. When we call copy on a stack block, the _Block_object_assign function is eventually called in the same way as ARC, except that the retained objects captured on the heap block are retained. __weak/__strong judgment is not made, because MRC does not have any of these two little brothers. When a block on the heap is about to be released, the _Block_object_dispose function is finally called, which calls the release method on the object captured by the block, thus freeing the block on the heap from holding on to the object (strong reference).

Finally, through a few illustrations, to illustrate [Block captures the auto variable of an object type】

This is how block captures the auto variable of the object type.





Block portal πŸ¦‹πŸ¦‹πŸ¦‹

Exploring the nature of blocks (1) — Basic knowledge

Explore the essence of blocks (2) — the underlying structure

Explore the essence of blocks (3) — basic type of variable capture

Explore the nature of blocks (4) — the type of Block

Exploring the nature of blocks (6) — An in-depth analysis of __blocks