I. Introduction to Block

Block objects are c-level syntax and runtime functionality. They are similar to standard C functions, but in addition to executable code, they may also contain variable bindings to automatic (stack) or managed (heap) memory. Thus, a block can maintain a set of states (data) that can be used to influence behavior when executed.

You can use blocks to form function expressions that can be passed to the API, optionally stored and used by multiple threads. A block is particularly useful as a callback because it contains both the code to execute on the callback and the data required during execution.

OS X V10.6 developer tools ship with blocks in GCC and Clang. You can use blocks in OS X V10.6 and later as well as iOS 4.0 and later. The block runtime is open source and can be found in LLVM’s compile-RT subproject repository. The block has also been submitted to the C Standard Working Group as N1370: Apple’s extension to C. Since both Objective-C and C++ are derived from C, blocks are designed to work with all three languages (and Objective-C++).

A Block is essentially an OC object that encapsulates a function call and its calling environment. It consists of an ISA pointer, an IMPL function pointer, and a descriptor. It’s kind of like the function pointer in C.

Block usage

Block basic expression:

/** * return_type specifies the returned object/keyword, etc. (void can be omitted) * blockName specifies the name of the block * var_type specifies the type of the argument (void can be, * varName indicates the parameter name */
return_type (^blockName)(var_type) = ^return_type (var_type varName) { ... };
blockName(var);
Copy the code

1. The Block syntax

1.1 When the return type isvoid

void (^blockName)(var_type) = ^void (var_type varName) { ... };
blockName(var);
// can be omitted into
void (^blockName)(var_type) = ^(var_type varName) { ... };
blockName(var);
Copy the code

1.2 When the parameter type isvoid

return_type (^blockName)(void) = ^return_type (void) {... }; blockName();// can be omitted into
return_type (^blockName)(void) = ^return_type { ... };
blockName();
Copy the code

1.3 When both the return type and parameter type arevoid

void (^blockName)(void) = ^void (void) {... }; blockName();// can be omitted into
void (^blockName)(void) = ^{ ... };
blockName();
Copy the code

1.4 the anonymous Block

When a Block is implemented, the right-hand side of the equals sign is an anonymous Block, which has no blockName and is called an anonymous Block.

^return_type (var_type varName) { ... };
Copy the code

1.5 typedefSimplify Block declarations

typedef return_type (^BlockTypeName)(var_type);
Copy the code

example

/ / declare
typedef void(^ClickBlock)(NSInteger index); 
/ / 1
@property (nonatomic.copy) ClickBlock clickBlock;
/ / 2
(void)methodHandle:(ClickBlock)handle { ... }
Copy the code

2. Application scenarios

2.1 Response Events

2.2 Data Transfer

2.3 Chain syntax

The idea is to take a Block as the return value of the method, of type the caller itself, and to take the method assetterForm return, so that you can achieve a continuous call, that is, chain programming.

Declare a method add in the CaculateMaker. H file

// CaculateMaker.h
@interface CaculateMaker : NSObject 

@property (nonatomic.assign) CGFloat result;

- (CaculateMaker *(^)(CGFloat num))add; 

@end
Copy the code

Implement the add method in CaculateMaker. M file

#import "CaculateMaker.h" 
@implementation CaculateMaker
- (CaculateMaker *(^)(CGFloat num))add { 
return ^CaculateMaker *(CGFloat num) {
        _result += num; 
        return self;
    };
} 
@end
Copy the code

Use the class

CaculateMaker *maker = [[CaculateMaker alloc] init];
maker.add(20).add(30);
Copy the code

Iii. Block in-depth exploration

1. The Block

In main.m we write the code:

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {        
        int age = 20;
        void (^block)(void) = ^ {NSLog(@"age is %d", age);
         };
        block();
    }
    return 0;
}
Copy the code

Compile the main.m file separately and run the following command from the terminal:

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
Copy the code

Get main. CPP file, we extract useful information from the file, tidy up the above code

int main(int argc, char * argv[]) {
    /* @autoreleasepool */ { 
        __AtAutoreleasePool __autoreleasepool; 
        int age = 20;
        void (*block)(void) = &__main_block_impl_0(
                                __main_block_func_0, 
                                &__main_block_desc_0_DATA,
                                age));
        block->FuncPtr(block);
    }
    return 0;
}
Copy the code

The contents of __main_block_IMPL_0 and __main_block_func_0 are

struct __block_impl { // __block_impl impl
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
static struct __main_block_desc_0 { // __main_block_desc_0* Desc
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0.sizeof(struct __main_block_impl_0)};

struct __main_block_impl_0 {
  struct __block_impl impl; //
  struct __main_block_desc_0* Desc; //
  int age;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) { // constructorimpl.isa = &_NSConcreteStackBlock; . }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int age = __cself->age; // bound by copy
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_3j_fvy599js5nnglvw7jslq09t80000gn_T_main_cef707_mi_0, age);
}
Copy the code

From the above we can see that the internal Block can access the external variables, which leads to the Block variable capture mechanism.

2. Variable capture of Block

2.1 Local variable auto

The local variable auto, the local variable that we normally write, has auto by default. Such as:

int age = 20; // equivalent to auto age = 20;
Copy the code

2.2 Local Variable static

Static modified local variables are not destroyed. As you can see from the following code, modifying static variables outside the Block can still affect the values inside the Block:

static int height  = 30;
int age = 20;
void (^block)(void) = ^ {NSLog(@"age is %d height = %d",age,height);
};
age = 25;
height = 35;
block();

// Execution result
age is 20 height = 35
Copy the code

Why has this changed? Because age is passed directly, height is passed *height, which means that the memory address is passed directly. You can follow the above instructions to try to compile into CPP files, to learn more. Disadvantages are permanent storage, large memory overhead.

2.3 Global Variables

Global variables cannot and do not need to be captured inside a Block because they are stored in the global static area and are accessed directly. The disadvantage is also large memory overhead.

3. The Block type

Block type The environment
__NSGlobalBlock__ The auto variable is not accessed
__NSStackBlock__ The auto variable is accessed
__NSMallocBlock__ __NSStackBlock__Call the copy

Each type of block calls copy as shown below

Block of the class The configuration storage domain of the copy source Print effect
_NSConcreteGlobalBlock The data area of the program Do nothing
_NSConcreteStackBlock The stack Copy from stack to heap
_NSConcreteMallocBlock The heap Reference count increment

When ARC is enabled, there are only blocks of type _NSConcreteGlobalBlock and _NSConcreteMallocBlock, because in ARC mode, The compiler automatically copies blocks on the stack to the heap as appropriate (blocks automatically invoke the copy method). The situation is as follows:

  • Block as a function return value;
  • Assign a block to__strongWhen the pointer;
  • Block is the Cocoa API nameusingBlockMethod parameters;
  • Block as a method parameter of the GCD API;

Therefore, the proposed Block declaration under ARC is written:

@property (strong.nonatomic) void (^block)(void);
@property (copy.nonatomic) void (^block)(void);
Copy the code

3.1 Block types are ultimately inherited fromNSObject

Let’s verify this with a piece of code:

void (^block)(void) =  ^{
     NSLog(@"Hello Block");
 };
NSLog(@"%@", [block class]);
NSLog(@"%@", [[block class] superclass]);
NSLog(@"%@", [[[block class] superclass] superclass]);
block();
Copy the code

The printed result is:

2021-04-10 01:24:01.844162+0800 studyOC[40328:4640930] Hello Block 2021-04-10 01:24:01.844502+0800 StudyOC [40328:4640930] __NSGlobalBlock__ 2021-04-10 01:24:01.844541+0800 studyOC[40328:4640930] NSBlock 2021-04-10 01:24:01. 844566 + 0800 studyOC NSObject [40328-4640930]Copy the code

Block types are inherited from NSBlock, and ultimately from NSObject. This validates __NSGlobalBlock__. Block validates all other types of Block. It is important to note that you should not use clang to convert OC code to C++ code to verify the above conclusion. In LLVM x.0, the compiled file is not a C++ file, but an intermediate file. The Runtime is involved, so the C++ code is a bit inaccurate.

3.2 Block internal access object typeautovariable

If the Block is on the stack, there is no strong reference to the auto variable; But if a Block is copied onto the heap,

  • The copy method inside the Block is called;

  • The copy method calls the _Block_object_assign function internally;

  • The _Block_object_assign function performs operations based on the __strong, __weak, and __unsafe_unretained modifier of the auto variable, for example, adding one to the reference count to form a retained or weak reference.

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

If a Block is removed from the heap,

  • Dispose function inside the Block is called.

  • The _Block_object_dispose function is called internally.

  • The _Block_object_dispose function automatically releases the referenced auto variable (release)

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

4. __blockThe modifier

__block wraps the modified variable into an object at compile time. To verify this, we convert the following code into C++ code:

__block int age = 18;
void (^block)(void) = ^ {NSLog(@"Hello Block %d", age);
 };
block();
Copy the code

It can be observed that age of type int is converted into an object by __block:

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding; // A pointer to oneself
 int __flags;
 int __size;
 int age;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_age_0 *age; // by ref. }Copy the code

__block can be used to solve the problem that the Block cannot modify the value of the auto variable; But __block cannot modify global or static variables.

4.1 __blockMemory management

  • When a Block is on the stack, there is no strong reference to the __block variable;

  • When a Block is copied to the heap, the copy method inside the Block is called; The copy method calls the _Block_object_assign function internally; The _Block_object_assign function forms a strong reference to the __block variable.

    _Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
    Copy the code
  • When a Block is removed from the heap, the Dispose method inside the Block is called. The _Block_object_dispose function is called internally. The _Block_object_dispose function automatically releases the referenced __block variable (release);

    _Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
    
    Copy the code

4.2 __block__forwardingPointer to the

5. Block circular reference

Circular references occur frequently when using blocks, so how does this circular reference occur and how to solve it? Let’s analyze.

5.1 Causes of Circular Reference

The problem with circular references is that two objects hold each other so that they cannot be freed.

Similarly, A circular reference occurs when A Block holds an object (strongly referenced) and an object holds A Block (strongly referenced). As A result, when object A is about to be released from the heap, object A still holds object B, and object B also holds object A, and it is stuck there, and no one can be released. As shown in figure:

5.2 How to Solve the problem of Circular Reference

So how do we solve this problem?

A. by__weak,__unsafe_unretainedMakes a Block a weak reference to an object
  • use__weakResolving circular references
    1. __weakThe modified object will behave automatically when it is releasednil;
    2. __weakModified object registered toautoreleasepool;
    3. __weakCan only be used in ARC mode and can only modify objects, not primitive data types.
__weak typeof(self) weakSelf = self;
self.block =  ^{
     NSLog(@"Hello Block, %p", weakSelf);
 };
self.block();
Copy the code

__weak has a certain impact on performance. When an object has a large number of __weak references, when the object is discarded, all objects whose __weak references have been assigned nil, consuming CPU resources. That being said, use it when you need it. For more information on the underlying principle of __weak, see this article: Objective-C weak keyword implementation source code parsing.

  • use__unsafe_unretainedResolving circular references
__unsafe_unretained id weakSelf = self;
self.block =  ^{
     NSLog(@"Hello Block, %p", weakSelf);
 };
self.block();
Copy the code

__weak only supports iOS 5.0+ and OS X Mountain Lion+ as deployed versions. __unsafe_unretained is more compatible. __unsafe_unretained and __weak both prevent holding, but __weak is an extremely safe behavior for a pointer object to be returned to nil when the object it points to is freed. __unsafe_unretained points to the same memory as the object, even after it has been destroyed. This can lead to a crash caused by accessing the freed object, but __unsafe_unretained doesn’t have as much of a performance impact.

B. use__blockResolve (Block must be called)
__block id weakSelf = self;
self.block =  ^{
     NSLog(@"Hello Block, %p", weakSelf);
    weakSelf = nil;
 };
self.block();
Copy the code

Through the use of__block, the relationship between Block and object becomes

When we’re done, manually set the weakly referenced object tonilSo their relationship becomes

Blocks and objects don’t hold each other.

Reference documentation

  1. Official Apple documentation for Blocks Programming Topics
  2. IOS Block In-depth Understanding (1
  3. Block usage in iOS, Examples, parsing and underlying principles (this is probably the most detailed Block parsing)
  4. How to modify block External variables inside a Block
  5. Objective-c weak keyword implementation source parsing