The introduction

In iOS daily development, blocks are used a lot. We don’t do startup optimization every day, and we don’t do performance optimization every day, but we may use blocks every day. This article focuses on the Block in the daily development of technical points worth our attention, we learn together.


Code specification

// Define a Block typedef returnType (^BlockName)(parameterA, parameterB,...) ; eg: typedef void (^RequestResult)(BOOL result); // instance ^{NSLog(@"This is a block"); }Copy the code

nature

A Block is essentially an Objective-C object, it also has an ISA pointer inside it, it’s an Objective-C object that encapsulates a function and its calling environment, and it can be added to collections like NSArray and NSDictionary, It is based on THE C language and runtime features, somewhat similar to standard C functions. But in addition to executable code, it also includes automatic binding of variables to the heap or stack.


Commonly used to introduce

  • Block types:
  1. NSGlobalBlock
void (^exampleBlock)(void) = ^{
    // block
};
NSLog(@"exampleBlock is: %@",[exampleBlock class]); 
Copy the code

ExampleBlock is: __NSGlobalBlock__ prints logs

If a block does not access external local variables, or accesses global variables, or static local variables, then the block is a global block and the data is stored in the global area.

  1. NSStackBlock
int temp = 100;
void (^exampleBlock)(void) = ^{
    // block
    NSLog(@"exampleBlock is: %d", temp);
};

NSLog(@"exampleBlock is: %@",[exampleBlock class]);
Copy the code

ExampleBlock is: __NSMallocBlock__?? What happened to __NSStackBlock__? Why print __NSMallocBlock__? Because we’re using ARC, Xcode does a lot of things for us by default.

Go to Build Settings, find Objective-C Automatic Reference Counting, set it to No, and Run the code again. You will see that the print log is: exampleBlock is: __NSStackBlock__

If a block accesses an external local variable, the block is a stack block and stored in the stack area. Since the release of the stack area is controlled by the system, memory is destroyed when the code in the stack is scoped out. If you call a block at this point, you will have a problem.

void (^simpleBlock)(void);
void callFunc() {
    int age = 10;
    simpleBlock = ^{
        NSLog(@"simpleBlock-----%d", age);
    };
}

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        callFunc();
        simpleBlock();
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return 0;
}
Copy the code

The following logs are generated: simpleBlock——–41044160

  1. NSMallocBlock

When a Block of type __NSStackBlock__ is copied from the stack to the heap, the Block on the heap is of type __NSMallocBlock__. In an ARC environment, the compiler automatically copies blocks from the stack to the heap, depending on the situation. There are four scenarios in which copy is performed:

  • Block as the return value of a function;
  • Block assigns to a __strong pointer, or to a member variable of block type;
  • Block is used as a Cocoa API method name that contains a method parameter called usingBlock;
  • Block as a method parameter of the GCD API;

  • The role of the __block

In simple terms, __block allows the block to access and modify external variables internally, and can be used to prevent circular references in ARC environments.

__block int age = 10;
void (^exampleBlock)(void) = ^{
    // block
    NSLog(@"1.age is: %d", age);
    age = 16;
    NSLog(@"2.age is: %d", age);
};
exampleBlock();
NSLog(@"3.age is: %d", age);
Copy the code

__block is used to solve the problem that the value of the auto variable cannot be changed inside the block. Why can’t the value of the auto variable be changed after adding the __block modifier?

This is because, with the __block modifier, the compiler wraps the __block variable into a structure __Block_byref_age_0, inside which *__forwarding is a pointer to itself, and inside which the external auto variable is stored.

struct __Block_byref_val_0 { void *__isa; // isa pointer __Block_byref_val_0 *__forwarding; int __flags; int __size; // Block struct size int age; // Captured variables}Copy the code

As you can see from the figure above, if the block is on the stack, the __forwarding pointer on the stack points to itself, and when the block is copied from the stack to the heap, the __forwarding pointer on the stack points to the __block structure copied to the heap. Age ->__forwarding->age assigns the heap age to 16. Age ->__forwarding->age Therefore, the __block structure on the stack or on the heap ends up using the data inside the __block structure on the heap.


  • The role of __weak

The simple answer is to prevent circular references.

Self makes a strong reference to the block itself, and the block makes a strong reference to self, which causes the problem of circular references. We can break the loop by using __weak to make the block object reference self weakly.

At this point we notice that since the block’s reference to self is an weak reference, it is possible that the self object itself will be freed when the block is executed. How do we ensure that the self object is not freed inside the block? This leads to the following function __strong.


  • The role of __strong

In simple terms, it prevents the external weak variable referenced inside the Block from being released in advance, and then the weak variable cannot be obtained inside the Block to continue to use.

__weak __typeof(self) weakSelf = self;
void (^exampleBlock)(void) = ^{
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    [strongSelf exampleFunc];
};
Copy the code

This ensures that a strongSelf object is held inside the block until the end of the block scope.

However, even so, there is still a scenario that executes __strong __typeof(weakSelf) strongSelf = weakSelf; Before, the weakSelf object was released, so if you send a message to the self object, that’s fine, and objective-C’s message sending mechanism allows you to send a message to a nil object, that’s fine.

But if you do something extra, like adding self to an array, then because self is nil, it’s going to Crash.

We can solve this problem by adding a layer of security, such as:

__weak __typeof(self) weakSelf = self;
void (^exampleBlock)(void) = ^{
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        // Add operation here
    }
};
Copy the code

Process knowledge

  • To consider

NSMutableString, NSMutableArray, NSMutableDictionary, NSMutableDictionary

NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
[mutableArray addObject:@"1"];
void (^exampleBlock)(void) = ^{
    // block
    [mutableArray addObject:@"2"];
};
exampleBlock();
NSLog(@"mutableArray: %@", mutableArray);
Copy the code

Print logs:

mutableArray: (

1,
2
Copy the code

)

The answer is: no. Because inside the block, we’re just using the memory address of the object mutableArray to add something to it. Its memory address is not changed, so it can execute correctly without using __block. When we only use the memory address of a local variable, rather than modifying its memory address, we do not need to add a __block to it. If we add a __block, the system will automatically create the corresponding structure, which is redundant and inefficient.

  • Block data structure

The internal data structure diagram of Block is as follows:

struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); }; struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...) ; struct Block_descriptor *descriptor; /* Imported variables. */ };Copy the code

Members of the Block_layout structure have the following meanings:

Isa: a pointer to the owning class, which is the type of the block

Flags: Indicates additional information about blocks in bits, such as determining the block type, determining the block reference count, and determining whether the block needs to execute auxiliary functions.

Reserved: reserved variables;

Invoke: a pointer to a block function that points to the function invocation address of a specific block implementation and executes the code inside the block.

Descriptor: Descriptor structure Block_descriptor, additional description of blocks, including copy/dispose function, block size, and reserved variables.

Variables: Because blocks have closures, you can access local variables outside the block. These variables are external local variables or the addresses of variables copied into the structure;

The members of the Block_descriptor structure have the following meanings:

Reserved: reserved variables;

Size: block size.

Copy: the function is used to capture variables and hold references;

Dispose: destructor used to release captured resources;


conclusion

There are four things to focus on when using blocks:

  1. Three types of blocks;
  2. Blocks avoid causing circular references;
  3. Block copies the auto variable.
  4. __block, __weak, __strong;

The above is the introduction of Block in this article. Thank you for reading.


References:

Working with Blocks


About The Technical Group

The iOS technology group is mainly used to learn and share the technology used in daily development, and keep learning and making progress together. The article repository is here: github.com/minhechen/i… Wechat official account: iOS Technology group, welcome to contact and exchange, thank you for reading.