1、 Block syntax
Return type (^ block name) (parameter type) = ^ return type (variable type variable name) {implementation}
When a block is defined directly, the return type at the time of definition can be omitted, i.e
Return type (^ block name) (parameter type) = ^ (variable type variable name) {implementation}
If the parameter type is void, it can be omitted to write as
Return type (^ block name) (void) = ^ {implementation}
Anonymous block: when a block is defined, the one to the right of the equal sign is an anonymous block
1.2 declaration of typedef simplified block
Typedef return type (^ block name) (parameter type);
1.3 block is an object
NSLog(@"%@",block);
Output this block through this%@
We can see that block is actually an object, which outputs the result__NSGlobalBlock__
It can be seen that the block is stored in the global area
2、 Circular application of block
Three methods to solve the circular reference of block
1、__ Weak to solve
self.name = @"hello";
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
block();
PS: in fact, many times,__strong
Will be omitted, so that under certain circumstances, data loss will occur.
For example, we enter a page, execute the code block, and then exit the page in less than three seconds. If not__strong
To modify the member variables after the secondary page is destroyedname
It is also destroyed and released. In this thread executed after three seconds,name
This member variable, throughgetter
Method, the return result will benull
2、__block
self.name = @"hello";
__block ViewController *vc = self;
self.block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
vc = nil; // Self - > block - > VC (NIL) - > (break circular reference) block
});
};
block();
3. It is solved through the block parameter
self.name = @"hello";
self.block = ^(ViewController *vc){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",vc.name);
});
};
self.block(self);
3、 Classification
-
Stack block
_NSConcreteStackBlock
: the block saved in the stack will be destroyed when the function returns. -
Heap block
_NSConcreteMallocBlock
Blocks saved in the heap will be destroyed when the reference count is 0. -
Global block
_NSConcreteGlobalBlock
: global static block. External global variables and static variables can be accessed without accessing external local variables in the block. This is nsglobalblock.
3、 Code + bottom analysis
- Check how the compiler implements block through the clang command, and enter clang – rewrite objc main m. Then it will generate main in the current directory CPP C + + files, but errors may be reported in this step, as follows:
In file included from /Users/apple/Desktop/Block_test/Block_test/ViewController.m:8:
/Users/apple/Desktop/Block_test/Block_test/ViewController.h:8:9: fatal error:
'UIKit/UIKit.h' file not found
#import <UIKit/UIKit.h>
^~~~~~~~~~~~~~~
1 error generated.
- It doesn’t matter. You can try this method. It still works. CD it to the current file directory, and then replace the previously executed command with:
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode. app/Contents/Developer/Platforms/iPhoneSimulator. platform/Developer/SDKs/iPhoneSimulator. sdk XXX. M file
success:

1、Demo1
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
void(^block)(void) = ^{
NSLog(@"hello world!");
};
block();
// Do any additional setup after loading the view.
}
@end
- Implementation of underlying code:
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
struct __ViewController__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __ViewController__viewDidLoad_block_desc_0* Desc;
//Constructor
__ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_db_80twdv6s18s_zk2z35wwh1cw0000gn_T_ViewController_8cc697_mi_0);
}
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
-
To make it easier for us to read the code, let’s familiarize ourselves with several rules:
① The names of structures and methods in C + + can generally be regarded as splicing layer by layer
②、xxx_ 0 means that this 0 represents the first, that is, the first
③ There are many strong conversions in C + +, so you can delete a certain amount of code. -
code analysis:
Through the bottom layer, we can see that the C + + implementation of block is a structure with two member variables(impl
、* Desc
)And a constructor (a function that initializes the structure).
Next, let’s look at his two member variables:
__block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
__block_impl
It is also a structure
*isa
: isa pointer, pointing to a class object, has three types:_ NSConcreteStackBlock、_ NSConcreteGlobalBlock、_ NSConcreteMallocBlock。
Flags
: load information (reference count and type information) of block, stored in bits.
Reserved
: keep variables.
*FuncPtr
: a pointer to the function called when the block is executed, that is, the code block to be executed by the block. In this case, yes__ViewController__viewDidLoad_block_func_0
Function.
__ViewController__viewDidLoad_block_desc_0
static struct __ViewController__viewDidLoad_block_desc_0 {
size_t reserved;
size_t Block_size;
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0)};
__ViewController__viewDidLoad_block_desc_0
Is a structure that contains two member variables:
Reserved: reserved space required for block version upgrade. Here, it is 0.
Block_ Size: block size (sizeof (struct _blocktest _block_impl_0)).
__ViewController__viewDidLoad_block_desc_0_DATA
It’s a__ViewController__viewDidLoad_block_desc_0
An example of.
__ViewController__viewDidLoad_block_func_0
__ViewController__viewDidLoad_block_func_0
It is the function called during the execution of block. The parameter is a__ViewController__viewDidLoad_block_impl_0
Pointer to type.
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_db_80twdv6s18s_zk2z35wwh1cw0000gn_T_ViewController_8cc697_mi_0);
}
Viewdidload method
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
void(*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
Code simplification and one-to-one correspondence
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
// [super viewDidLoad];
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
// void(^block)(void) = ^{NSLog(@"hello world");};
void(*block)(void) = &__ViewController__viewDidLoad_block_impl_0
(
__ViewController__viewDidLoad_block_func_0,
&__ViewController__viewDidLoad_block_desc_0_DATA
);
// block();
block->FuncPtr(block);
}
- The second line of code: defines the block.
We see that the block becomes a pointer to a pass__ViewController__viewDidLoad_block_impl_0
Constructor instantiated structure__ViewController__viewDidLoad_block_impl_0
example,__ViewController__viewDidLoad_block_impl_0
Two parameters are required during initialization:
__ViewController__viewDidLoad_block_func_0
: function pointer to the block.
__ViewController__viewDidLoad_block_desc_0_DATA
: initialize as a static global variable__ViewController__viewDidLoad_block_desc_0
Structure instance pointer for
- The third line of code: block is called
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)
adoptblock->FuncPtr
Pointer found__blockTest_block_func_0
Function and convert to(void (*)(__block_impl *))
Type.
((__block_impl *)block)
Then pass the block as a parameter to the function call.
Simply put, block calls XXXX_ func_ 0 method and passed itself.
Add: flags,__block_impl
Purpose of parameters
hereBlock_private.hCan seeFlags
Specific information:
// Values for Block_layout->flags to describe block objects
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
BLOCK_NEEDS_FREE = (1 << 24), // runtime
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
BLOCK_IS_GC = (1 << 27), // runtime
BLOCK_IS_GLOBAL = (1 << 28), // compiler
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30), // compiler
BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
};
quoteOn the rewritten block structure of block (1) – clangInterpretation of:
That is, in general, a
block
The flags member of is set to 0 by default. If whenblock
needBlock_copy()
andBlock_release
This kind of copy auxiliary function will be set to1 << 25
, that isBLOCK_HAS_COPY_DISPOSE
Type. You can search a lotBlock_copy
Method blog, which involvesBLOCK_HAS_COPY_DISPOSE
。
To summarize the usage of enumeration classes, the first 16 bits not only serve as markers, but also record reference counts:
- BLOCK_DEALLOCATING: release the tag. Commonly used block_ NEEDS_ Free performs bit and operation, and passes in flags together to tell the block that it can be released.
- BLOCK_REFCOUNT_MASK: generally involved in judging reference count. It is an optional parameter.
- BLOCK_NEEDS_FREE: set the enumeration bit to tell the block that it can be released. It is intended to explain that a block is a heap block, which we often say_ NSConcreteMallocBlock 。
- BLOCK_HAS_COPY_DISPOSE: whether there is a copy helper function.
- BLOCK_HAS_CTOR: whether it has a block destructor (dispose function).
- BLOCK_IS_GC: whether to enable GC mechanism (garbage collection).
- BLOCK_HAS_SIGNATURE: and block_ USE_ Relative to string, judge whether the current block has a signature. Used for dynamic invocation at runtime.
Demo1 summary
The above is the bottom implementation of the most basic block. In fact, we have only taken the first step here. You want when we use__ block 、__ What happens when weak and block call external variables? How is the bottom layer realized? How are variables captured?
Demo2 probe block intercepts variables
Intercept auto variable value

We see that modifying variables directly in the block will prompt an error. Why?
void blockTest()
{
int num = 10;
void (^block)(void) = ^{
NSLog(@"%d",num);
};
num = 20;
block();
}
int main(int argc, char * argv[]) {
@autoreleasepool {
blockTest();
}
}
The print result is 10. The rewritten code of Lang is as follows:
struct __blockTest_block_impl_0 {
struct __block_impl impl;
struct __blockTest_block_desc_0* Desc;
int num;
__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int _num, int flags=0) : num(_num) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
int num = __cself->num; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_3c2714_mi_0,num);
}
void blockTest()
{
int num = 10;
void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, num));
num = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
__blockTest_block_impl_0
One more member variableint num;
, look at the constructor__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int _num, int flags=0)
, you can see that the third parameter is only the value of the variable, which explains why 10 is printed because the block intercepts the value. The pointer was not intercepted.
Modify variables with static
void blockTest()
{
static int num = 10;
void (^block)(void) = ^{
NSLog(@"%d",num);
num = 30;
};
num = 20;
block();
NSLog(@"%d",num);
}
The internal variable is 20, and the result can be printed at the same time. The rewritten code of clang is as follows:
struct __blockTest_block_impl_0 {
struct __block_impl impl;
struct __blockTest_block_desc_0* Desc;
int *num;
__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int *_num, int flags=0) : num(_num) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
int *num = __cself->num; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_0,(*num));
(*num) = 30;
}
void blockTest()
{
static int num = 10;
void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, &num));
num = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_1,num);
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
__blockTest_block_impl_0
One more member variableint *num;
, different from the above, the block interceptsPointer
Therefore, the value of the variable can be modified internally through the pointer, and the value of the variable can be modified externally, and the block can also “perceive”. So why didn’t you pass the pointer before? Because the variable is on the stack and the scope is within the function blocktest, it is possible that the variable is destroyed before the block. At this time, there will be a problem if the block accesses the variable through the pointer. The static modified variable will not be destroyed, so you don’t have to worry.
global variable
int num = 10;
void blockTest()
{
void (^block)(void) = ^{
NSLog(@"%d",num);
num = 30;
};
num = 20;
block();
NSLog(@"%d",num);
}
The print result is 20, 30. The rewritten code of clang is as follows:
int num = 10;
struct __blockTest_block_impl_0 {
struct __block_impl impl;
struct __blockTest_block_desc_0* Desc;
__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_1875c6_mi_0,num);
num = 30;
}
Very simple, in initialization__blockTest_block_impl_0
It doesn’t take num as a parameter,__blockTest_block_func_0
It is also a direct access to global variables.
Summary:
Variable type | Capture inside the block | Access mode |
---|---|---|
Local auto variable | yes | pass by value |
Local static variable | yes | Pointer passing |
global variable | no | Direct access |
Use__ Block modifier variable
void blockTest()
{
__block int num = 10;
void (^block)(void) = ^{
NSLog(@"%d",num);
num = 30;
};
num = 20;
block();
NSLog(@"%d",num);
}
The effect is the same as using static to modify variables. The rewritten code of Lang is as follows:
struct __Block_byref_num_0 {
void *__isa;
__Block_byref_num_0 *__forwarding;
int __flags;
int __size;
int num;
};
struct __blockTest_block_impl_0 {
struct __block_impl impl;
struct __blockTest_block_desc_0* Desc;
__Block_byref_num_0 *num; // by ref
__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, __Block_byref_num_0 *_num, int flags=0) : num(_num->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
__Block_byref_num_0 *num = __cself->num; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_0,(num->__forwarding->num));
(num->__forwarding->num) = 30;
}
static void __blockTest_block_copy_0(struct __blockTest_block_impl_0*dst, struct __blockTest_block_impl_0*src) {_Block_object_assign((void*)&dst->num, (void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __blockTest_block_dispose_0(struct __blockTest_block_impl_0*src) {_Block_object_dispose((void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __blockTest_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __blockTest_block_impl_0*, struct __blockTest_block_impl_0*);
void (*dispose)(struct __blockTest_block_impl_0*);
} __blockTest_block_desc_0_DATA = { 0, sizeof(struct __blockTest_block_impl_0), __blockTest_block_copy_0, __blockTest_block_dispose_0};
void blockTest()
{
__attribute__((__blocks__(byref))) __Block_byref_num_0 num = {(void*)0,(__Block_byref_num_0 *)&num, 0, sizeof(__Block_byref_num_0), 10};
void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, (__Block_byref_num_0 *)&num, 570425344));
(num.__forwarding->num) = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_1,(num.__forwarding->num));
}
Wow, it’s hard, brother Dei. How can so many things come out? It doesn’t matter. Analyze it slowly.
__blockTest_block_impl_0
One more member variable__Block_byref_num_0 *num;
, we saw the passing__block
The modified variable type becomes a structure__Block_byref_num_0
,__blockTest_block_impl_0
One more member variable__Block_byref_num_0 *num;
, block captures__Block_byref_num_0
Type pointer,
__Block_byref_num_0
We see__Block_byref_num_0
Is a structure and has aisa
Therefore, we can know that it is actually an object. There is also one__Block_byref_a_0 *
Type__forwarding
andnum
,num
We can guess that it is used to save the value of the variable,__forwarding
It’s a little complicated. I’ll talk about it later.
__blockTest_block_copy_0and__blockTest_block_dispose_0
__blockTest_block_copy_0
Is called in_Block_object_assign
,__blockTest_block_dispose_0
Is called in_Block_object_dispose
。
function | Call timing |
---|---|
__blockTest_block_copy_0 |
__ When the block variable structure instance is copied from the stack to the heap |
__blockTest_block_dispose_0 |
__ When the block variable structure instance reference count is 0 |
about_Block_object_assign
and_Block_object_dispose
More detailed codes can be found inruntime.c View in.
// Runtime support functions used by compiler when generating copy/dispose helpers
// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
// see function implementation for a more complete description of these fields and combinations
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
BLOCK_FIELD_IS_WEAK = 16, // declared __weak, only used in byref copy helpers
BLOCK_BYREF_CALLER = 128, // called from __block (byref) copy/dispose support routines.
};
-
BLOCK_FIELD_IS_OBJECT
: OC object type -
BLOCK_FIELD_IS_BLOCK
: it’s a block -
BLOCK_FIELD_IS_BYREF
: on the stack__ Variable modified by block -
BLOCK_FIELD_IS_WEAK
: be__ The variable modified by weak is only in block_ Byref is used when managing internal object memory -
BLOCK_BYREF_CALLER
: process block_ Byref is an extra mark that will be added when the internal object is stored in memory (tell the internal implementation not to retain or copy)
__blockTest_block_desc_0
We can see that it has two more callback function pointers*copy
and*dispose
, these two pointers will be assigned__main_block_copy_0
and__main_block_dispose_0
Finally, we see that accessing num is as follows:
__Block_byref_num_0 *num = __cself->num; // bound by ref
(num->__forwarding->num) = 30;
Let’s talk about why.
Memory management of block
As we mentioned earlier__block_impl
directive_NSConcreteStackBlock
There are actually three types of class objects of type:
type | Storage area |
---|---|
_NSConcreteStackBlock |
Stack |
_NSConcreteGlobalBlock |
Data area |
_NSConcreteMallocBlock |
heap |
As mentioned earliercopy
anddispose
, in arc environment, under what circumstances will the compiler automatically copy the block on the stack from the stack to the heap?
Copy block from stack to heap |
---|
When calling copy instance method of block |
When block is returned as the return value of the function |
When passing a block in the cocoa method with usingblock or the API of GCD |
Assign block to__ StrongWhen the ID type or block type of the modifier |
WhenBock
Copy from stack to heap,__block
It also changes:

WhenBlock
On the stack,__block
The storage domain of is stack,__block
The variable is on the stackBlock
Hold.
WhenBlock
When copied to the heap, theBlock
Internalcopy
Function,copy
The function will be called internally_Block_object_assign
Function. here__block
The storage domain of variables is heap,__
The block variable is on the heapBlock
Hold.
When on the pileBlock
Is released, will callBlock
Internaldispose
,dispose
The function will be called internally_Block_object_dispose
, on the pile__block
Released.

- When on multiple stacks
Block
Using on stack__block
Variables,__block
Variables are used by multiple on the stackBlock
Hold. - When
Block0
When copied to the heap,__block
It will also be copied to the heapBlock0
Hold.Block1
Still holding on to the stack__block
, on the original stack__block
Variable__forwarding
Refers to the data after copying to the heap__block
Variable. - When
Block1
Also copied to the heap when the__block
Piled upBlock0
andBlock1
Only, and__block
Reference count of + 1. - When on the pile
Block
Were released,__block
Variable structure instance reference count is 0, call_Block_object_dispose
, on the pile__ Block is released.
The following figure is a description__forwarding
Change. That explains__forwarding
Meaning of existence:
__ Forwarding ensures that the corresponding variables can be accessed correctly on the stack or heap

int main(int argc, char * argv[]) {
int num = 10;
NSLog(@"%@",[^{
NSLog(@"%d",num);
} class]);
void (^block)(void) = ^{
NSLog(@"%d",num);
};
NSLog(@"%@",[block class]);
}
Print results:
2019-05-04 18:40:48.470228+0800 BlockTest[35824:16939613] __NSStackBlock__
2019-05-04 18:40:48.470912+0800 BlockTest[35824:16939613] __NSMallocBlock__
- We can see the first one
Block
Not assigned to__strong
Pointer, and the secondBlock
Assign to__strong
Pointer, so the first one is on the stack and the second one is on the heap.
Block intercept object (auto variable of object type)
1. Define a simple block:
MJBlock block;
{
MJPerson *person = [[MJPerson alloc] init];
person.age = 10;
block = ^{
NSLog(@"---------%d", person.age);
};
}
NSLog(@"------");
- After the second nslog is printed, the person will not be destroyed, because the block has a pointer to the external person object. The block is on the heap and is of malloc type. If the block is not destroyed, the person will not be destroyed. However, if the MRC environment is changed, the block on the stack will not strongly reference the auto object. But if person uses__ If weak is modified, person will be destroyed first.
Change the above block to the following:
MJBlock block;
{
MJPerson *person = [[MJPerson alloc] init];
person.age = 10;
__weak MJPerson *weakPerson = person;
block = ^{
NSLog(@"---------%d", weakPerson.age);
};
}
NSLog(@"------");
- In this case, use the previous one
clang xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
Errors will be reported when converting to the underlying C + + codecannot create __weak reference in file using manual reference
Because weak is a weak reference and is made at runtime, it is usedxcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
After conversion:
typedef void (*MJBlock)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
MJPerson *__weak person;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, MJPerson *__strong _person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
MJPerson *__strong person = __cself->person; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_c41e64_mi_0, ((int (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("age")));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->person, (void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->person, 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};
Previous mjpherson * person; Also converted to mjperson*__ weak person;
The summary is as follows:
- When the auto variable of the object type is accessed inside the block
- 1. If the block is on the stack, neither the strong pointer nor the weak pointer will make a strong reference to the auto object.
- 2. If the block is copied to the heap. In the arc environment, when a block is strongly referenced, the copy operation will be carried out. If the block is copied, the internal function will be called_ Block_ object_ Assign function. Calling this function will modify the keyword mjperson according to the external auto object*__ Weak person or mjperson*__ Strong person refers to auto strongly or weakly
-
3. If the block is removed from the heap. The dispose function inside the block will be called, and the dispose function will be called internally_ Block_ object_ Dispose function_ Block_ object_ The dispose function automatically releases the referenced auto variable, similar to the release operation.
image.png
Interview questions:
MJPerson *p = [[MJPerson alloc] init];
__weak MJPerson *weakP = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1-------%@", p);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2-------%@", weakP);
});
});
NSLog(@"-----------");
After the transformation of the positions of nslog1 and nslog2, it is concluded that the release time of person depends on when the strong reference of person is released, regardless of the release time of weak reference
There are some differences between the declared references of block in arc and MRC
- Blocks can be stored on the stack or in the heap
- By default, it is stored in the stack, and there is no need to manage memory
- The block stored in the heap will retain the block
- (MRC) when a block is in the heap, you do not want to perform a retain operation on the block, preceded by__ block
- (ARC) preceded by__ Weak or__ unsafe_ unretained
- __ Weak and__ unsafe_ The difference between unretained:__ Weak will assign nil to the object when it is released, and the latter will not
- Block_ Copy transfers the block in the stack to the heap and performs a retain operation on the object that the block will reference
- Avoid the retain operation on the object referenced by the block, and precede the declaration of the referenced object__ block
Get reference count under arc (retain count)
Note: the following methods can only be used for debugging, and the return value is not 100% trusted in the case of multithreading.
1. Use KVC
[obj valueForKey:@"retainCount"]
2. Use private API
OBJC_EXTERN int _objc_rootRetainCount(id);
_objc_rootRetainCount(obj)
3. Use cfgetretaincount
CFGetRetainCount((__bridge CFTypeRef)(obj))
source
Link:https://www.jianshu.com/p/221d0778dcaa
Link:https://www.jianshu.com/p/60c0bc161201
Link:https://blog.csdn.net/weixin_37547351/article/details/105106559
Link:https://blog.csdn.net/zhangwenhai001/article/details/46702271
Link:https://blog.csdn.net/iuyo89007/article/details/51761720/