This is the 10th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

What is a Block (fast implementation of direct input inlink)

A block is a special type of data


The role of the Block

  • Used to hold a piece of code that can be fetched and called at an appropriate time
  • Functions are similar to functions and methods

The format of the Block

Return value (^block variable name) (parameter list) = ^(parameter list){};Copy the code
  • No parameter No return value

    void (^sunBlock)();
        sunBlock = ^{
            NSLog(@"sunBlock");
        };
        sunBlock();
    Copy the code
  • Yes Parameter No return value

    void(^sunBlock)(int,int);
    sunBlock = ^(int value1,int value2){
        NSLog(@"%d",value1 + value2);
    };
    sunBlock(10.20);
    Copy the code
  • There are parameters and return values

    int (^sunBlock)(int,int);
    sunBlock = ^(int value1,int value2){
        return value1 + value2;
    };
    NSLog(@"%d",sunBlock(10.20));
    Copy the code

Typedef and Block

Use a typedef to alias a block. Like a pointer to a function, the name of the block variable is the alias

typedef int (^calculateBlock)(int,int);
int main(int argc, const char * argv[]) {
    
    calculateBlock sumBlock  = ^(int value1,int value2){
        return value1 + value2;
    };
    NSLog(@"%d",sumBlock(20.10));
        
    calculateBlock minusBlock  = ^(int value1,int value2){
        return value1 - value2;
    };
    NSLog(@"%d",minusBlock(20.10));
}
Copy the code

The underlying implementation of Block

  • The original file:

    int main(int argc, const char * argv[]){^ {};return 0;
    }
    Copy the code
  • To see the underlying implementation of Block, use the clang command to convert OC to C++ code. Use CD to locate the folder where the main.m file is, and then use clang-rewrite-objc M converts OC to C++ and generates a main. CPP file in the same directory as main.m

    struct __block_impl {
        void *isa; //isa, pointer to the owning class, that is, the type of the block
        int Flags; // Flags, the flag variable, is used when implementing the internal operation of the block
        int Reserved; //Reserved, the variable is Reserved
        void *FuncPtr; // The pointer to the function called when the block executes
    };
    
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
            impl.isa = &_NSConcreteStackBlock;  // The ISA pointer to __main_block_IMPL_0 points to _NSConcreteStackBlock
            impl.Flags = flags;
            impl.FuncPtr = fp; The function FuncPtr of __main_block_impl_0 refers to the function __main_block_func_0
            Desc = desc; // __main_block_desc_0_DATA is created when __main_block_desc_0 is defined, which records the size of the block structure.}};static void __main_block_func_0(struct __main_block_impl_0 *__cself){}static struct __main_block_desc_0 {
        size_t reserved; // Reserved fields
        size_t Block_size; // Block size (struct __main_block_impl_0))
    } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    // While defining the __main_block_desc_0 structure, the above code creates and assigns a value to __main_block_desc_0_DATA so that __main_block_IMPL_0 can be initialized in the main function.
    
    int main(int argc, const char * argv[]){((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
        return 0;
    }
    Copy the code

    The __main_block_IMPL_0 structure is named after the function (main) and the sequence (0th) in which it occurs. The __main_block_IMPL_0 structure is named after the function (main). If it is a global BLCOk, it is named according to the variable name and sequence of occurrences. __main_block_IMPL_0 contains two member variables and a constructor. The member variables are __block_impl structure and description Desc. The constructor then initializes block type information and function pointer information. 4. The function __main_block_func_0 takes a parameter __cself, which is the block itself. The more valuable information is the size of the block. 6. The main function created the block, so we can see that executing the block is calling a function that takes the block itself as an argument, which corresponds to the body of the block.


The classification of the Block

  • NSConcreteGlobalBlockGlobal staticblockWithout accessing any external variables.
  • NSConcreteStackBlockIt’s stored on the stackblockIs destroyed when the function returns.
  • NSConcreteMallocBlockSaved in the heapblockIs destroyed when the reference count reaches 0.

NSConcreteGlobalBlock implementation of block type

void (^testGlobalBlock)() = ^{
    NSLog(@"hello block");
};
int main(int argc, const char * argv[]) {
    testGlobalBlock();
    return 0;
}
Copy the code

The ISA of testGlobalBlock points to _NSConcreteGlobalBlock, which is created in the global domain and the block variable is stored in the global data store

NSConcreteStackBlock implementation of block type

int main(int argc, const char * argv[]) {
    void (^testStackBlock)() = ^{
        NSLog(@"hello block");
    };
    testStackBlock();
    return 0;
}
Copy the code

The ISA of testStackBlock points to _NSConcreteStackBlock, which is created on the stack.

NSConcreteMallocBlock implementation of block type

int main(int argc, const char * argv[]) {
   void (^testStackBlock)() = [^{
        NSLog(@"hello block");
    } copy];
    testStackBlock();
    return 0;
}
Copy the code

Blocks of type NSConcreteMallocBlock are not usually found in the source code directly. They need to be copied from _NSConcreteStackBlock (that is, they need to be copied before they can be stored in the heap).

Internally, it copies the contents of the block from the stack to the heap with the memmove function, and makes ISA point to _NSConcreteMallocBlock.

The main knowledge of blocks comes from the transfer of blocks on the stack to blocks on the heap.


The application of the Block

Block to access local variables

  • inBlockAccess local variables in
    int main(int argc, const char * argv[]) {
        int testNum = 10;
        void(^testNumBlock)() = ^{
                NSLog(@"%d",testNum);
        };
        testNumBlock();
        return 0; } print result:10
    Copy the code
  • In a statementBlockAfter that, callBlockBefore making changes to local variables, in the callBlockWhen the local variable value is the old value before modification
    int main(int argc, const char * argv[]) {
        int testNum = 10;
        void(^testNumBlock)() = ^{
            NSLog(@"%d",testNum);
        };
        testNum = 20;
        testNumBlock();
        return 0; } print result:10
    Copy the code
  • inBlockYou cannot modify local variables directly in
    int main(int argc, const char * argv[]) {
        int testNum = 10;
        void(^testNumBlock)() = ^{
            testNum = 20; / / an error
            NSLog(@"%d",testNum);
        };
        testNumBlock();
        return 0;
    }
    Copy the code

Access a local variable within a Block that is decorated with __block

  • Use an underscore before a local variableblockModify, in the statementBlockAfter, callBlockBefore making changes to local variables, in the callBlockWhen the local variable value is the new value after modification
    __block int testNum = 10;
        void(^testNumBlock)() = ^{
            NSLog(@"%d",testNum);
        };
        testNum = 20; testNumBlock(); Print result:20
    Copy the code
  • Use an underscore before a local variableblockDecorate, inBlockYou can modify local variables directly in
    int main(int argc, const char * argv[]) {
        __block int testNum = 10;
        void(^testNumBlock)() = ^{
            testNum = 20;
            NSLog(@"%d",testNum);
        };
        testNumBlock();
        return 0; } print result:20
    Copy the code

Block access to global variables

  • inBlockGlobal variables can be accessed in
    int testNum = 10;
    int main(int argc, const char * argv[]) {
        void(^testNumBlock)() = ^{
            NSLog(@"%d",testNum);
        };
        testNumBlock();
        return 0; } print result:10
    Copy the code
  • In a statementBlockAfter, callBlockBefore making changes to global variables, in the callBlockThe global variable value is the new value after modification
    int testNum = 10;
    int main(int argc, const char * argv[]) {
        void(^testNumBlock)() = ^{
            NSLog(@"%d",testNum);
        };
        testNum = 20;
        testNumBlock();
        return 0; } print result:20
    Copy the code
  • inBlockYou can modify global variables directly in
    int testNum = 10;
    int main(int argc, const char * argv[]) {
        void(^testNumBlock)() = ^{
            testNum = 20;
            NSLog(@"%d",testNum);
        };
        testNumBlock();
        return 0; } print result:20
    Copy the code

Block to access static variables

  • inBlockStatic variables can be accessed in
    int main(int argc, const char * argv[]) {
        static int testNum = 10;
        void(^testNumBlock)() = ^{
            NSLog(@"%d",testNum);
        };
        testNumBlock();
        return 0; } print result:10
    Copy the code
  • In a statementBlockAfter, callBlockBefore making changes to static variables, in the callBlockThe static variable value is the new value after modification
    int main(int argc, const char * argv[]) {
        static int testNum = 10;
        void(^testNumBlock)() = ^{
            NSLog(@"%d",testNum);
        };
        testNum = 20;
        testNumBlock();
        return 0; } print result:20
    Copy the code
  • inBlockYou can modify static variables directly in
    int main(int argc, const char * argv[]) {
        static int testNum = 10;
        void(^testNumBlock)() = ^{
            testNum = 20;
            NSLog(@"%d",testNum);
        };
        testNumBlock();
        return 0; } print result:20
    Copy the code

Block is passed as an argument

typedef void(^TestBlock)();
NSMutableArray *array;
void test(){    
    int a = 10;
    TestBlock blcok = ^{
        NSLog(@"%d",a);
    };
    [array addObject:blcok];
    NSLog(@"% @",blcok);
}

int main(int argc, const char * argv[]) {

    array = [[NSMutableArray alloc]init];
    test();
    TestBlock blockk = [array lastObject];
    blockk();
    NSLog(@"% @",blockk);
    return 0; } result: under ARC: test2[2423:124143] <__NSMallocBlock__: 0x1004037f0>
test2[2423:124143] 10
test2[2423:124143] <__NSMallocBlock__: 0x1004037f0> Under non-ARC: program crashes test2[2449:125851] <__NSStackBlock__: 0x7fff5fbff6f8>
Copy the code

In non-ARC cases, TestBlock’s ISA points to __NSStackBlock__. When the function exits, the corresponding heap is destroyed and the block no longer exists. After copy or retain, The type of the object changed from __NSStackBlock__ to __NSMallocBlock__, which can still be accessed after the function has ended. In non-ARC environments, copy or retain blocks must be released after use, otherwise there will be a memory leak, and the leak point is at the system level. There is no problem trigger in the Instruments.

In the ARC case, the system copies the block that captured the external variable. So the return type is __NSMallocBlock__, which is still accessible after the function ends

If the code in blcok does not access the variable:

TestBlock blcok = ^{
        NSLog(@"demo"); }; Results: The ARC and non-ARC results are consistent test2[2484:128052] <__NSGlobalBlock__: 0x100005290>
test2[2484:128052] demo
test2[2484:128052] <__NSGlobalBlock__: 0x100005290>
Copy the code

Block as the return value

  • In the ARC
- (testBlcok) myTestBlock {
    __block int val = 10;
    return ^{
        NSLog(@"val = %d", val); }; } 错 误 : Returning block that lives on the local stackCopy the code

When you pass a block out, you have to pass it out to an autorelease object on the heap.

- (testBlcok) myTestBlock {
    __block int val = 10;
    return [[^{
        NSLog(@"val = %d", val);
    } copy] autorelease];
}
Copy the code
  • In the ARC
- (testBlcok) myTestBlock {
    __block int val = 10;
    return ^{
        NSLog(@"val = %d", val); }; } Result: normalCopy the code

In ARC, when a block is returned as an argument, the block is automatically moved to the heap.

Block as property

ARC and non-ARC declarations are the same

@property (strong, nonatomic) TestBlock *strongBlock;
@property (copy, nonatomic) TestBlock *copyBlock;
Copy the code

Block memory management under MRC and ARC

Block memory management under MRC

  • By default,BlockMemory is stored in the stack and does not need to be memory managed by the developer
    - (void)viewDidLoad {
        [super viewDidLoad];
        void(^testBlock)() = ^{
            NSLog(@"-- -- -- -- -- -"); }; testBlock(); } Result: When the testBlock variable is out of scope, the testBlock memory is automatically freedCopy the code
  • inBlockMemory is stored in the stack when, if inBlockIs a reference to an external object, and nothing is done to the referenced object
    - (void)viewDidLoad {
        [super viewDidLoad];
        Student *stu = [[Student alloc]init]; 
        void(^testBlock)() = ^{
            NSLog(@"% @",stu); }; testBlock(); [stu release]; } result: Student can be released normallyCopy the code
  • If theBlockTo make acopyOperation, soBlockMemory is moved to the heap and needs to be processedreleaseOperations to manage memory
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        void(^testBlock)() = ^{
            NSLog(@"testBlock"); }; testBlock(); Block_copy(testBlock); Block_release(testBlock); } Result: The Block is released normallyCopy the code
  • If theBlockTo make acopyOperation, soBlockMemory will be moved to the heap atBlockMemory is stored in the heap when, if inBlockIf an external object is referenced in theretainOperate, even inBlockCalled itselfreleaseAfter that,BlockIt does not do one on the referenced objectreleaseOperation, which can cause a memory leak
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        Student *stu = [[Student alloc]init];
        void(^testBlock)() = ^{
            NSLog(@"% @",stu); }; testBlock(); Block_copy(testBlock); Block_release(testBlock); [stu release]; } Result: Student cannot be released properly because it has been retained in the BlockCopy the code
  • If theBlockTo make acopyOperation, soBlockMemory will be moved to the heap atBlockMemory is stored in the heap when, if inBlockIf an external object is referenced in theretainOperation, so that the referenced object is not performed onceretainAction that can be used in front of an object__blockTo modify
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        __block Student *stu = [[Student alloc]init];
        void(^testBlock)() = ^{
            NSLog(@"% @",stu); }; testBlock(); Block_copy(testBlock); Block_release(testBlock); [stu release]; } result: Student can be released normallyCopy the code
  • If there is one inside the objectBlockProperty, while inBlockIf the object is accessed internally, a circular reference is created
  • Case one
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    @end
    ----------------------------------------------
    @implementation Student
    -(void)dealloc{
    NSLog(@"%s",__func__);
       Block_release(_testBlock);
       [super dealloc];
    }
    @end
    ----------------------------------------------
    -(void)viewDidLoad {
       [super viewDidLoad];
    
       Student *stu = [[Student alloc]init];
       stu.testBlock = ^{
           NSLog(@"% @",stu); }; stu.testBlock(); [stu release]; } Result: Because testBlock is a Student attribute and is modified with the copy modifier (to ensure that the Block is in the heap, so that the Block is not released in the stack), the Block will retain the Student object once, so that the circular reference cannot be releasedCopy the code
    • Case two
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)(); - (void)resetBlock;
    @end
    ----------------------------------------------
    @implementation Student
    -(void)resetBlock{
    
        self.testBlock = ^{
            NSLog(@"% @",self); }; } - (void)dealloc{
        NSLog(@"%s",__func__);
    
        Block_release(_testBlock);
        [super dealloc];
    }
    @end
    ----------------------------------------------
    -(void)viewDidLoad {
        [superviewDidLoad]; Student *stu = [[Student alloc]init]; [stu resetBlock]; [stu release]; } Result: The Student object cannot be freed properly here. Although it appears that one alloc for one release is consistent with memory management rules, in fact, in the resetBlock method implementation, self is retained inside the Block, so that the circular reference cannot be freedCopy the code
  • If there is one inside the objectBlockProperty, while inBlockIf the object is accessed internally, a circular reference is created. The solution is to use an underscore before the objectblockTo avoidBlockOn the objectretainoperation
    • Case one
      @interface Student : NSObject
      @property (nonatomic,copy) void(^testBlock)();
      @end
      ----------------------------------------------
      @implementation Student
      -(void)dealloc{
          NSLog(@"%s",__func__);
          Block_release(_testBlock);
          [super dealloc];
      }
      @end
      ----------------------------------------------
      - (void)viewDidLoad {
          [super viewDidLoad];
          __block Student *stu = [[Student alloc]init];
          stu.testBlock = ^{
              NSLog(@"% @",stu); }; stu.testBlock(); [stu release]; } result: Student can be released normallyCopy the code
    • Case two
      @interface Student : NSObject
      @property (nonatomic,copy) void(^testBlock)(); - (void)resetBlock;
      @end	
      ----------------------------------------------
      @implementation Student
      -(void)resetBlock{
      __block typeof(self) stu = self; __block typeof(self) stu = self;
      __block Student *stu = self;
      self.testBlock = ^{
      NSLog(@"% @",stu); }; } - (void)dealloc{
      NSLog(@"%s",__func__);
      Block_release(_testBlock);
      [super dealloc];
      }
      @end
      ----------------------------------------------
      - (void)viewDidLoad {
      [superviewDidLoad]; Student *stu = [[Student alloc]init]; [stu resetBlock]; [stu release]; } result: Student can be released normallyCopy the code

Block memory management under ARC

  • By default in ARC,BlockMemory is stored in the heap, and ARC automatically manages the memory, so programmers just need to avoid circular references
    - (void)viewDidLoad {
        [super viewDidLoad];
        void(^testBlock)() = ^{
            NSLog(@"testBlock"); }; testBlock(); } Result: When the Block variable is out of scope, the memory of the Block is automatically freedCopy the code
  • inBlockMemory is stored in the heap when, if inBlockThere is a strong reference to the object referenced inBlockWhen freed, the strong reference to the object is automatically removed, so there is no memory leak
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        Student *stu = [[Student alloc]init];
        void(^testBlock)() = ^{
            NSLog(@"% @",stu); }; testBlock(); } result: Student can be released normallyCopy the code
  • If there is one inside the objectBlockProperty, while inBlockIf the object is accessed internally, a circular reference is created
    • Case one
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    @end
    ----------------------------------------------
    @implementation Student
    -(void)dealloc{
        NSLog(@"%s",__func__);
    }
    @end
    ----------------------------------------------
    -(void)viewDidLoad {
        [super viewDidLoad];
        Student *stu = [[Student alloc]init];
        stu.testBlock = ^{
            NSLog(@"% @",stu); }; stu.testBlock(); } Result: Because testBlock is a Student property that is modified with the copy modifier (to ensure that the Block is in the heap, so that the system does not release the Block on the stack), the Block makes a strong reference to the Person object, and the circular reference cannot be releasedCopy the code
  • Case two
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    - (void)resetBlock;
    @end
    ----------------------------------------------
    @implementation Student
    - (void)resetBlock{
        self.testBlock = ^{
        NSLog(@"-- -- -- -- -- - % @", self); }; } - (void)dealloc{
        NSLog(@"%s",__func__);
    }
    @end
    ----------------------------------------------
    - (void)viewDidLoad {
        [superviewDidLoad]; Student *stu = [[Student alloc]init]; [stu resetBlock]; } Result: the Student object cannot be freed. In the testBlock method implementation, there is a strong reference to self inside the Block, causing the circular reference not to be freedCopy the code
  • If there is one inside the objectBlockProperty, while inBlockIf the object is accessed internally, it will cause a circular reference. The way to solve the circular reference is to use a weak reference pointer to the object and thenBlockThis weak reference pointer is used internally for operations, thus avoidingBlockMake a strong reference to an object
  • Case one
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)();
    @end
    ----------------------------------------------
    @implementation Student
    -(void)dealloc{
        NSLog(@"%s",__func__);
    }
    @end
    ----------------------------------------------
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        Student *stu = [[Student alloc]init];
        __weak typeof(stu) weakS = stu;
    
        stu.testBlock = ^{
            NSLog(@"-- -- -- -- -- - % @", weakS);
        };
        stu.testBlock();
    
        // The Student object can be released normally here
    }
    Copy the code
  • Case two
    @interface Student : NSObject
    @property (nonatomic,copy) void(^testBlock)(); - (void)resetBlock;
    @end
    ----------------------------------------------
    @implementation Student
    -(void)resetBlock{
        Weak Typeof (self) weakP = self; weak typeof(self) weakP = self;
        __weak Student *stu = self;
        self.testBlock = ^{
        NSLog(@"-- -- -- -- -- - % @", self);
        };
    }
    void)dealloc{
        NSLog(@"%s",__func__);
    }
    @end
    ----------------------------------------------
    -(void)viewDidLoad {
        [superviewDidLoad]; Student *stu = [[Student alloc]init]; [stu resetBlock]; } result: Student can be released normallyCopy the code

Note: The underline block keyword is different under MRC and ARC

__block has two functions under MRC

  1. Allows for theBlockTo access and modify local variables
  2. banBlockImplicitly refers to the objectretainoperation

__block has only one effect under ARC

  1. Allows for theBlockTo access and modify local variables