This “iOS Fish Weekly issue 18” brings the interview analysis is the variable capture mechanism of block. By understanding the variable capture mechanism of blocks, we can better deal with memory management and avoid memory leaks caused by improper use.

Block variable capture mechanism

The variable capture mechanism of the block is to ensure that the internal block can normally access the external variables.

Variable types Whether the inside of the block is captured access
The global variable no Direct access to the
Local variable (type Auto) is Value passed
Local variable (static type) is Pointer passed

For global variables, they are not captured inside the block and are accessed directly. Because of scope, global variables can be accessed directly anywhere, so they are not captured. For local variables, they cannot be accessed directly from the outside, so they need to be captured. Let’s take a look at how block captures local variables.

A local variable of type auto

For local variables of type auto (all variables defined by us are of type Auto by default, but are omitted), a member variable of the same type is automatically generated inside the block to store the value of the variable. Access to this variable is passed by value. A local variable of type Auto may be destroyed, its memory will disappear, and the block will not be able to access that memory when executing code in the future, so it will capture its value. Since this is value passing, we can change the value of the variable captured outside the block without affecting the value of the variable captured inside the block.

A local variable of type static

A local variable of static type is automatically generated inside the block. It is used to store the address of the variable. Access to the variable is passed through a pointer. Static variables are always stored in memory, so just capture their address. Conversely, since we are passing Pointers, modifying the value of the captured variable outside the block affects the value of the captured variable inside the block.

A local variable of the object type

For local variables of object type, a block is captured along with its ownership modifier.

  • If the block is on the stack, there will be no strong reference to the object
  • If the block is copied to the heap, the inside of the block will be calledcopy(__funcName_block_copy_num)Function, which is called inside copyassign(_Block_object_assign)Function, assign will operate on the variable’s ownership modifier to form a strong or weak reference.
  • If a block is removed from the heap, that is, when it’s released, it’s called inside the blockdispose(_Block_object_dispose)Function, dispose function will automatically release the referenced variable.

For variables decorated with __block

For variables decorated with __block (which can be used to solve the problem of not being able to modify the value of the auto variable inside the block), the compiler wraps the __block variable as an __Block_byref_varName_num object. Its memory management is almost equivalent to accessing the auto variable of the object type, but there are differences.

  • If the block is on the stack, it will not be right__blockVariables generate strong references
  • If a block is copied to the heap, the copy function inside the block is called, and the copy function inside the block is called assign__blockVariables form a strong reference (retain).
  • If a block is removed from the heap, the dispose function inside the block is called and the reference is automatically removed__blockVariable (release).

Memory management for object types decorated with __block:

  • if__blockThe variable is on the stack and will not generate a strong reference to the object to which it points
  • if__blockThe variable is copied to the heap and will be called__blockintra-variablecopy(__Block_byref_id_object_copy)The copy function internally calls the assign function, which in turn acts on the property modifier of the variable to form a strong or weak reference (retain). (Note: only retain under ARC but not under MRC, so you can still pass under MRC__blockSolve the circular reference problem)
  • if__blockThe variable is removed from the heap__blockThe dispose function inside the variable automatically releases the pointed object.

Avoid memory leaks when using blocks

By understanding the variable capture mechanism of blocks, we can better deal with memory management and avoid memory leaks caused by improper use.

A common block loop reference is self(obj) -> block -> self(obj). The reason the block strongly references self here is that for local variables of object type, the block is captured along with its ownership modifier, and the default ownership modifier for an object is __strong.

self.block = ^{
    NSLog(@ "% @".self);
};
Copy the code

Why does it say self is a local variable? Because self is an implicit argument to the OC method.

To avoid circular references, we can use __weak, where the block will no longer hold self.

__weak typeof(self) weakSelf = self;
self.block = ^{
    NSLog(@ "% @", weakSelf);
};
Copy the code

To avoid early release of self during a block call, we can use __strong to hold self during block execution, which is known as weak-strong-dance.

__weak typeof(self) weakSelf = self;
self.block = ^{
    __strong typeof(self) strongSelf = weakSelf;
    NSLog(@ "% @", strongSelf);
};
Copy the code

Of course, we usually use more than @Weakify (self) and @Strongify (self).

@weakify(self);
self.block = ^{
    @strongify(self);
    NSLog(@ "% @".self);
};
Copy the code

If you use RAC’s weak-strong-dance, you can also do this:

@weakify(self, obj1, obj2);
self.block = ^{
    @strongify(self, obj1, obj2);
    NSLog(@ "% @".self);
};
Copy the code

If the block is nested:

@weakify(self);
self.block = ^{
    @strongify(self);
    self.block2 = ^{
        @strongify(self);
        NSLog(@ "% @".self); }};Copy the code

Do you wonder why @Weakify (self) is no longer needed internally? This problem is left to you to think and solve!

Rather than simply circular references to each other, large ring references created by blocks require more care and insight, such as:

TYAlertView *alertView = [TYAlertView alertViewWithTitle:@"TYAlertView" message:@"This is a message, the alert view containt text and textfiled. "];
[alertView addAction:[TYAlertAction actionWithTitle:@ "cancel" style:TYAlertActionStyleCancle handler:^(TYAlertAction *action) {
    NSLog(@ % @ - % @ "".self, alertView); }]].self.alertController = [TYAlertController alertControllerWithAlertView:alertView preferredStyle:TYAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
Copy the code

There are two circular references here:

  1. self -> alertController -> alertView -> handlerBlock -> self
  2. alertView -> handlerBlock -> alertView

Avoid circular references:

TYAlertView *alertView = [TYAlertView alertViewWithTitle:@"TYAlertView" message:@"This is a message, the alert view containt text and textfiled. "];
@weakify(self, alertView);
[alertView addAction:[TYAlertAction actionWithTitle:@ "cancel" style:TYAlertActionStyleCancle handler:^(TYAlertAction *action) {
    @strongify(self, alertView);
    NSLog(@ % @ - % @ "".self, alertView); }]].self.alertController = [TYAlertController alertControllerWithAlertView:alertView preferredStyle:TYAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
Copy the code

Use block implicitly retains self; use block implicitly retains self; use block implicitly retains self; Explicitmention ‘self’ to indicate this is intended behavior.

The reason is that using _variable directly in a block results in an implicit strong reference to self in the block. Xcode believes that this may implicitly lead to circular references, which may cause trouble for developers, and it is really difficult to check if you don’t look carefully. I have been looking for this circular reference for a long time, and I even invited my tutor to find out the reason. So the warning is to explicitly use self in a block so that the block explicitly retains self. Use self->_variable or self.variable.

If you use @Weakify and @Strongify it really doesn’t cause circular references because @Strongify declares the variable name as self. So if you use weak Typeof (self) weak_self = self; How about strong typeof(weak_self) strong_self = weak_self?