Blocks as an implementation of closures in Objective-C play an important role in iOS development, especially as callbacks. How Do I Declare A Block in Objective-C this article describes the implementation of blocks

The essence of the Block

Blocks are called anonymous functions with automatic variables (local variables). Block syntax is very similar to C functions. In fact, the bottom layer of a Block is processed as C source code. The compiler that supports Block will convert the source code containing Block syntax into the source code that the C compiler can process and compile as C source code.

The LLVM compiler clang can convert syntax containing blocks to C++.

clang -rewrite-objc fileName
Copy the code

For example, a very simple code Block:

#include <stdio.h>

int main() {
	void (^blk)(void) = ^{
		printf("Hello Block! \n");
	};

	blk();
	return 0;
}
Copy the code

After converting it to C++ source using clang, the core content is as follows:

struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; Struct __main_block_impl_0 {struct __block_impl impl; struct __main_block_desc_0* Desc; // constructor __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; Static void __main_block_func_0(struct __main_block_impl_0 *__cself) {printf("Hello Block! \n"); Static struct __main_block_desc_0 {size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; intmain() {/ / call __main_block_impl_0 constructor void series (*) (void) = ((void (*) ()) & __main_block_impl_0 (__main_block_func_0 (void *), &__main_block_desc_0_DATA)); / / a series () call ((void (__block_impl *) (*)) ((__block_impl *) series) - > FuncPtr) ((__block_impl *) series);return 0;
}
Copy the code

This section of source code mainly contains three struct and two functions, is actually C language source code:

  • struct __block_impl
  • struct __main_block_impl_0
  • struct __main_block_desc_0
  • *static void __main_block_func_0(struct __main_block_impl_0 __cself)
  • int main()

It’s easy to see that the mian() function was the mian function in the original code, and the __main_block_func_0 function was the Block syntax in the original code:

^ {printf("Hello Block! \n");
};
Copy the code

It can be concluded that:

  • Anonymous functions used by blocks are actually treated as simple C functions
  • Clang names functions (__main_block_func_0) based on the name of the function to which the Block syntax belongs (mian in this case) and the order in which the Block syntax appears in the function (0 in this case).
  • The argument **__cself is the variable ** pointing to the Block value, which is the equivalent of self in Objective-C.

Next, focus on the __main_block_IMPL_0 structure

The __main_block_func_0 function’s argument of type __cself is declared as struct __main_block_IMPL_0. __main_block_IMPL_0 is the Block’s data structure definition, which contains the impl member variable and the Desc pointer. The IMPL __block_IMPl structure declaration contains flags, fields needed for future version upgrades, and function Pointers:

struct __block_impl { void *isa; int Flags; // some flags int Reserved; Void *FuncPtr; void *FuncPtr; // function pointer};Copy the code

The Desc pointer contains the Block size:

static struct __main_block_desc_0 { size_t reserved; Size_t Block_size; // Block size};Copy the code

The impl and Desc member variables are called in the __main_block_IMPL_0 constructor, which is called in the mian function, with the conversion removed for ease of reading:

/ / call __main_block_impl_0 constructor void series (*) (void) = ((void (*) ()) & __main_block_impl_0 (__main_block_func_0 (void *), &__main_block_desc_0_DATA)); Struct __main_block_impl_0 TMP = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA); struct __main_block_func_0 *blk = &tmp;Copy the code

The arguments used in the constructor are the function pointer __main_block_func_0 and the __main_block_desc_0 struct instance pointer initialized by the static global variable __main_block_desc_0_DATA:

static struct __main_block_desc_0 __main_block_desc_0_DATA = { 
    0, 
    sizeof(struct __main_block_impl_0)
};
Copy the code

From these calls, the following two items can be summarized:

  • The source code assigns an automatic variable of type __main_block_IMPL_0, that is, a struct instance pointer generated on the stack, to a variable of type __main_block_IMPL_0.
  • The source code is initialized with a Block, the size of an instance of the __main_block_IMPL_0 structure.

Expand the __main_block_IMPL_0 structure:

struct __main_block_impl_0 {
  	void *isa;
  	int Flags;
  	int Reserved;
  	void *FuncPtr;
  	struct __main_block_desc_0* Desc;
};
Copy the code

Constructor initialization data in this program is as follows:

isa = &_NSConcreteStackBlock;
Flags = 0;
Reserved = 0;
FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;
Copy the code

FuncPtr = __main_block_func_0 is simply used to call function __main_block_func_0 and print Hello Block! Statement, which is the implementation of the block call in the original source code:

blk();
Copy the code

Its corresponding source code after removing the conversion is very clear:

/ / a series () call ((void (__block_impl *) (*)) ((__block_impl *) series) - > FuncPtr) ((__block_impl *) series); FuncPtr (* BLK ->impl.FuncPtr)(BLK);Copy the code

At this point, the creation and use of blocks can be understood as follows:

  • When you create a Block, you are essentially declaring a struct and initializing its member variables.
  • When a Block is executed, it calls a function through a function pointer.

There is a _NSConcreteStackBlock in the __main_block_IMPL_0 constructor:

impl.isa = &_NSConcreteStackBlock;
Copy the code

This ISA pointer is very reminiscent of the ISA pointer in Objective-C. In Objective-C classes and objects, each object has an ISA pointer, and objective-C classes are ultimately converted to structs, and the member variables in that class are declared as members of the structure, and all kinds of structures are class_T structures based on the objC_class structure.

typedef struct Objc_object {
	Class isa;
} *id;

typedef struct obje_class *Class;

struct objc_class {
	Class isa;
};

struct class_t {
	struct class_t *isa;
	strcut class_t *superclass;
	Cache cache;
	IMP *vtable;
	uintptr_t data_NEVER_USE;
};
Copy the code

In objective-C an object generated by a class is actually an instance of a structure like a structure that generates an object generated by that class. Each generated object (that is, each struct instance of the generated object) holds a struct instance pointer to that class through the member variable ISA.

For example, a TestObject class with member variables valueA and valueB:

@interface TestObject : NSObject {
    int valueA;
    int valueB;
}
@end
Copy the code

The structure of the object of its class is as follows:

struct TestObject{
	Class isa;
	int valueA;
	int valueB;
};
Copy the code

** In Objective-C, each class (such as NSObject, NSMutableArray) generates and holds instances of the class_T structure for each class. ** This instance holds declared member variables, method names, method implementations (that is, function Pointers), attributes, and Pointers to the parent class, and is used by the Objective-C runtime library.

The __main_block_IMPL_0 structure is equivalent to an Objective-C class object based on the Objc_object structure, where the member variable ISA is initialized to ISA = &_NSConcreteStackBlock; , _NSConcreteStackBlock is equivalent to an instance of a calss_t structure, and information about this class is placed in _NSConcreteStackBlock when the Block is processed as an Objective-C object.

In fact, the essence of a Block a Block is an Objective-C object.

Block captures automatic variables

How does Blocks capture automatic variables

One of the reasons blocks are used as an alternative to traditional callback functions is that: Block allows access to local variables and captures the value of the variable used, i.e. the instant value of the automatic variable. For example, in this code, the instant value of the local variable mul is stored in the block, so subsequent changes to muL do not affect the value of the muL stored in the block:

int mul = 7;
int (^blk)(int) = ^(int num) {
    return mul * num;
};

// change mul
mul = 10;

int res = blk(3);
NSLog(@"res:%d", res); // res:21 not 30
Copy the code

Let’s use Clang to see what happens to the structure of a Block after it captures an automatic variable:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int mul; // Automatic variables used in Block syntax expressions are appended as member variables to the __main_block_impl_0 structure. __main_block_IMPL_0 (void *fp, struct __main_block_desC_0 * DESC, int _mul, int flags=0); mul(_mul) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int mul = __cself->mul; // bound by copyprintf("mul is:%d\n", mul);
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main() {
  int mul = 7;
  void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, mul));

  ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

 return 0;
}
Copy the code

An analysis of __main_block_IMPL_0 and its constructor shows that the automatic variable MUL used in the Block is appended to the __main_block_IMPL_0 structure as a member variable and initialized according to the parameters of the passing constructor. Struct instance initialization in __main_block_IMPL_0 is as follows:

impl.isa = &_NSConcreteStackBlock; impl.Flags = 0; impl.FuncPtr = __main_block_func_0; Desc = &__main_block_desc_0_DATA; mul = 7; // Append the member variableCopy the code

Thus, ** In the __main_block_IMPL_0 struct instance (that is, Block), the automatic variable value is captured. ** The Block capture of automatic variables can be summarized as follows: When a Block executes a syntax, the values of automatic variables used in the Block are stored in the struct instance of the Block (that is, the Block itself). That is, append member variables to the __main_block_IMPL_0 structure.

__block specifier

While a Block can capture automatic variable values, it cannot modify them, as in the following code:

int main() {
	int val = 10;
	void(^blk)(void) = ^ {
		val = 1; // error Variable is not assignable (missing __block type specifier)
	};
	blk();
	printf("val:%d\n", val);
	
	return 0;
}
Copy the code

Use the __block specifier for val:

int main() {
	__block int val = 10;
	void(^blk)(void) = ^ {
		val = 1; 
	};
	blk();
	printf("val:%d\n", val); // val:1
	
	return 0;
}
Copy the code

After converting it with clang:

struct __Block_byref_val_0 { void *__isa; __Block_byref_val_0 *__forwarding; int __flags; int __size; int val; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_val_0 *val; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_val_0 *val = __cself->val; // bound by ref (val->__forwarding->val) = 1; } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) { _Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/); } static void __main_block_dispose_0(struct __main_block_impl_0*src) { _Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/); } 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}; intmain__block int val = 10; __attribute__((__blocks__(byref))) __Block_byref_val_0 val = { (void*)0, (__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10 }; void(*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);printf("val:%d\n", (val.__forwarding->val));

 return 0;
}
Copy the code

The source code increases dramatically with the addition of the __block variable, most notably by adding a structure and four functions:

  • struct __main_block_impl_0
  • static void __main_block_copy_0
  • static void __main_block_dispose_0
  • _Block_object_assign
  • _Block_object_dispose

First compare the pair changes of the __main_block_func_0 function with and without __block

__block static void __main_block_func_0(struct __main_block_impl_0 *__cself) {int mul = __cself->mul; // bound by copyprintf("mul is:%d\n", mul); } // Use __block static void __main_block_func_0(struct __main_block_impl_0 *__cself) {__Block_byref_val_0 *val = __cself->val; // bound by ref (val->__forwarding->val) = 1; }Copy the code

Int mul = __cself->mul; int mul = __cself->mul; .

Val, which uses the __block variable, becomes an instance of a structure.

// __block int val = 10; After transformation of the source code: __attribute__ ((__blocks__ (byref))) __Block_byref_val_0 val = {0, & val, 0, sizeof (__Block_byref_val_0), 10};Copy the code

The __block variable also becomes an automatic variable of the __Block_byref_val_0 structure type (the stack generated __Block_byref_val_0 structure instance), which is initialized to 10, and this value is also present in the initialization of the structure instance. Represents that the structure holds a member variable equivalent to the original automatic variable (the following member variable val in the __Block_byref_val_0 structure is equivalent to the original automatic variable) :

struct __Block_byref_val_0 { void *__isa; __Block_byref_val_0 *__forwarding; int __flags; int __size; int val; // A member variable equivalent to the original automatic variable};Copy the code

Go back to the code where Block assigns a value to val:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref

  (val->__forwarding->val) = 1;
 }
Copy the code

The __forwarding member of the __Block_byref_val_0 structure instance holds a pointer to the instance itself. Access the member variable val via the member variable __forwarding.

The __Block_byref_val_0 structure of the __block variable is not written directly into the Block’s __main_block_IMPL_0 structure so that the same __block variable can be used in multiple blocks. For example, using the same __block variable in two blocks:

__block int val = 10;
void (^blk1)(void) = ^ {
	val = 1; 
};

void (^blk2)(void) = ^ {
	val = 2;
};
Copy the code

After conversion:

__Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(    __Block_byref_val_0), 10};

blk1 = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, &val, 570425344);

blk2 = &__main_block_impl_1(__main_block_func_1, &__main_block_desc_1_DATA, &val, 570425344);
Copy the code

Now that you have a general idea of why blocks can capture automatic variables, a few questions remain:

  1. Why is the member variable __forwarding needed in __Block_byref_val_0?
  2. What are the __main_block_copy_0 and __main_block_dispose_0 functions for?

The type of the Block

To sum up:

  • Automatic variable that converts Block to Block struct type
  • An automatic variable of structure type to which a __block variable is converted

An automatic variable of a struct type is an instance of that struct generated on the stack.

Since a Block is an Objective-C object, what kind of object is it? The isa pointer in a Block refers to the Class of that Block, which is of the _NSConcreteStackBlock type. In the Runtime of this Block, there are six concretestackblock types:

  • _NSConcreteStackBlock: Block created on the stack
  • _NSConcreteGlobalBlock: Block as a global variable
  • _NSConcreteMallocBlock: Block created on the heap

They correspond to memory allocation in the program:

So under what circumstances is a Block on the heap? When was it on the stack? When is it global?

_NSConcreteGlobalBlock

_NSConcreteGlobalBlock Makes sense. When using a Block as a global variable, the generated Block is the _NSConcreteGlobalBlock class object. Such as:

#include <stdio.h>

void (^blk)(void) = ^{
	printf("Gloabl Block\n");
};

int main() {
	return 0;
}
Copy the code

After the clang conversion, initialize the Block with the __block_impl member variable to _NSConcreteGlobalBlock, which is set in the program memory data area with the struct instance:

isa = &_NSConcreteGlobalBlock;
Copy the code

The reason for storing global blocks in a data area is: ** Automatic variables cannot be used where global variables are used, so there is no capture of automatic variables. So the contents of a Block’s struct instance do not depend on the state at execution, so only one instance is needed in the entire program. ** Only when capturing automatic variables does the value that a Block captures with a struct instance change depending on the state at execution time. Therefore, Block is _NSConcreteGlobalBlock:

  • Block is used as a global variable
  • When automatic variables that should be captured are not used in a Block syntax expression

_NSConcreteStackBlock

The Block syntax generates a Block as an _NSConcreteStackBlock class object and sets it on the stack, except in the above two cases where the Block is configured in the program’s data area. A Block configured on the stack is automatically discarded if the scope of the variable to which it belongs ends.

_NSConcreteMallocBlock

So when is the _NSConcreteMallocBlock class configured on the heap used?

Blocks configured on global variables can also be accessed by Pointers from outside the variable scope. But blocks placed on the stack are discarded if their scope ends. The __block variable is also configured on the stack, and is discarded if the scope of the variable to which it belongs ends. Block and __block variables need to be copied to the heap to protect them from the end of the variable field.

Blocks provide methods for copying blocks and __block variables from the stack to the heap. Block copied to the heap writes the _NSConcreteMallocBlock class object to the Block using the constructor instance’s member variable ISA:

isa = &_NSConcreteMallocBlock;
Copy the code

Access to __block on the heap is implemented by __forwarding: ** the __block variable uses the structure member variable __forwarding to access the __block variable correctly whether it is configured on the stack or on the heap. ** When a __block variable is configured on the heap, the stack member variable __forwarding refers to an instance of the heap, then the __block variable can be accessed correctly from either the stack or the heap.

And during ARC, in most cases the compiler knows to automatically copy blocks from the stack to the heap when appropriate, such as when a Block is returned. When a Block is passed to a method or function argument, the compiler cannot determine that it needs to manually call the copy method to copy the Block on the stack to the heap, but some of the methods provided by Apple already copy the passed argument internally in the appropriate place, so manual copying is no longer necessary:

  • Methods in Cocoa framework with usingBlock in their names;
  • By the API

And no matter where the Block configuration is, copying with copy is no problem. But copying blocks from the stack to the heap can be CPU intensive. Calling copy on a Block that is already on the heap increases its reference count.

And __block variables are also affected when a Block using a __block variable is copied from the stack to the heap: if a __block variable is used in a Block, when the Block is copied from the stack to the heap, those __block variables are also copied from the stack to the heap. And the Block holds the __block variable. If a Block uses a __block variable, when any Block is copied from the stack to the heap, the __block variable is copied to the heap and held by the Block; When the remaining blocks are copied from the stack to the heap, the copied Block holds the __block variable and increases its reference count. If blocks configured on the heap are deprecated, it says the __block variable used is also released. This is the same way of thinking about objective-C memory management. Even if a Block that uses a __block variable holds that __block variable, when the Block is deprecated, the __block variable it holds is also deprecated.

Block capture object

Where Block captures a variable val of type __block, what’s the difference if it captures an Objective-C object?

int main() {
    id arr = [[NSMutableArray alloc] init]; 
    void (^blk)(id) = [^(id obj) {
    	[arr addObject:obj];
	NSLog(@"arr count: %ld", [arr count]);
    } copy];

    blk(@"Objective-C");
    blk(@"Switf");
    blk(@"C++");

    return 0;
}
Copy the code

It’s worth noting that the Block captures an Objective-C object and calls the method addObject: that changes that object, so no compilation errors occur here. This is because the value of the variable captured by the block is an object of the NSMutableArray class, which is described in C language as a pointer to the structure instance used to capture the NSMutableArray class object. The addObject method uses the value of the automatic variable arr intercepted by the block, so there is no problem, but assigning a value to the captured arR object inside the block causes an error:

int main() { id arr = [[NSMutableArray alloc] init]; void (^blk)(id) = [^(id obj) { arr = [NSMutableArray arrayWithObjects:obj, nil];  // error Variable is not assignable (missing __blocktype specifier)
        
        NSLog(@"arr count: %ld", [arr count]);
    } copy];

    blk(@"Objective-C");
    return 0;
}

Copy the code

After the previous code conversion part of the source code is:

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
	_Block_object_assign((void*)&dst->arr, (void*)src->arr, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
	_Block_object_dispose((void*)src->arr, 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
	};
Copy the code

__block int val = 10; Part of the converted source code:

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
	_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
	_Block_object_dispose((void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);
}

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*);
}
Copy the code

Objective-c objects and __block variables are used in the same struct. The differences are as follows: Objective-c objects are identified with BLOCK_FIELD_IS_OBJECT and __block variables are identified with BLOCK_FIELD_IS_BYREF. That is, BLOCK_FIELD_IS_OBJECT and BLOCK_FIELD_IS_BYREF are used to distinguish the object type of copy function and Dispose function from that of __block variable.

The source code in the __main_block_DESc_0 structure added member variables copy and dispose, as well as the __main_block_copy_0 function and __main_block_dispose_0 function assigned to the member variable as a pointer, these two functions function:

  • The _Block_object_assign function used in the __main_block_copy_0 function copies the object type object to the Block structure’s member variable arr and holds the object. Calling the _Block_object_assign function is equivalent to the retain function, Assigns an object to a structure member variable of the object type.

  • The _Block_object_dispose function is used in the __main_block_dispose_0 function to dispose the object assigned to the structure member arr of the Block. Calling _Block_object_dispose is equivalent to calling release to release the object assigned in the object type structure.

These two functions are called when a Block is copied from the stack to the heap and when a Block already on the heap is discarded:

  • Copy is called on the Block stack to copy to the heap
  • The dispose function is called when a Block on the heap is discarded

Precautions for using Block

When a Block is copied from the stack to the heap, it holds the captured object, which makes it easy to create circular references. For example, if a Block is referenced in self and the Block captures self, it causes a circular reference, which the compiler can usually detect:

@interface TestObject : NSObject
@property(nonatomic, copy) void (^blk)(void);
@end

@implementation TestObject
- (instancetype)init {
    self = [super init];
    if (self) {
        self.blk = ^{
            NSLog(@"% @", self); // warning:Capturing 'self' strongly in this block is likely to lead to a retain cycle
        };
    }
    return self;
}
Copy the code

Similarly, capturing a member variable of the current object also creates a reference to self. For example, in the following code, the Block uses the name member variable of the self object, in effect capturing self. To the compiler, name is only a member variable of the structure used by the object:

@interface TestObject : NSObject
@property(nonatomic, copy) void (^blk)(void);
@property(nonatomic, copy) NSString *name;
@end

@implementation TestObject
- (instancetype)init {
    self = [super init];
    if (self) {
        self.blk = ^{
            NSLog(@"% @", self.name);
        };
    }
    return self;
}
@end
Copy the code

There are two ways to resolve circular references:

  1. Declare self using __weak

    - (instancetype)init {
        self = [super init];
        if (self) {
            __weak typeof(self) weakSelf = self;
            self.blk = ^{
                NSLog(@"% @", weakSelf.name);
            };
        }
        return self;
    }
    Copy the code
  2. Use temporary variables to avoid referencing self

    - (instancetype)init {
        self = [super init];
        if (self) {
            id tmp = self.name;
            self.blk = ^{
                NSLog(@"% @", tmp);
            };
        }
        return self;
    }
    Copy the code

A weak reference to an object in a Block is made after it has been modified with the __weak modifier:

reference

  • Ibireme Objc block
  • How do blocks in DraP iOS hold objects
  • Document Blocks Programming Topics
  • Objective-c Advanced Programming: Multithreading and Memory Management for iOS and OS X