Block portal π¦π¦π¦
Exploring the nature of blocks (1) — Basic knowledge
Explore the essence of blocks (2) — the underlying structure
Explore the essence of blocks (3) — basic type of variable capture
Explore the nature of blocks (4) — the type of Block
Exploring the nature of blocks (6) — An in-depth analysis of __blocks
Let’s start with a code example
* * * * * * * * * * * * * * * * * * * * *CLPerson.h*********************
#import <Foundation/Foundation.h>
@interface CLPerson : NSObject
@property (nonatomic.assign) int age;
@end* * * * * * * * * * * * * * * * * * * * *CLPerson.m*********************
#import "CLPerson.h"
@implementation CLPerson- (void)dealloc {
NSLog(@"%s",__func__);
}
@end
*********************main.m*********************
#import <Foundation/Foundation.h>
#import "CLPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool{{// The temporary scope starts
CLPerson *person = [[CLPerson alloc] init];
person.age = 10;
}// The temporary scope ends
NSLog(@"-----------flag1");
}
return 0;
}
Copy the code
By printing the mark
flag1
Breakpoint debugging can be seen in the temporary scopeperson
It is easy to understand that objects are released as soon as they go out of scope.
Add block to the above code and adjust it as follows
* * * * * * * * * * * * * * * * * * * * *CLPerson.h*********************
#import <Foundation/Foundation.h>
@interface CLPerson : NSObject
@property (nonatomic.assign) int age;
@end* * * * * * * * * * * * * * * * * * * * *CLPerson.m*********************
#import "CLPerson.h"
@implementation CLPerson- (void)dealloc {
NSLog(@"%s",__func__);
}
@end
*********************main.m*********************
#import <Foundation/Foundation.h>
#import "CLPerson.h"
typedef void(^CLBlock) (void);/ / β β β
int main(int argc, const char * argv[]) {
@autoreleasepool {
CLBlock myBlock;/ / β β β
{// The temporary scope starts
CLPerson *person = [[CLPerson alloc] init];
person.age = 10;
myBlock = ^{/ / β β β
NSLog(@"---------%d",person.age);
};
}// The temporary scope ends
NSLog(@"-----------flag1");
}
return 0;
}
Copy the code
Print the tag againflag1
At breakpoint debug runAnd it tells us that out of the temporary scope,
person
The object was not released. There are two caveats:
- Because now it’sARCThe environment,
myBlock
Is a strong pointer, therefore, in theblockObject assigned tomyBlock
When pointer, the compiler will automaticallyblockObject to performcopy
Operation, so when the assignment is complete,myBlock
It’s pointing at a heap spaceblockObject copy.
Iphoneos clang-arch arm64-rewrite-objc main.m -o main. CPP
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
CLPerson *person;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, CLPerson *_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) {
CLPerson *person = __cself->person; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_7__p19yp82j0xd2m_1k8fpr77z40000gn_T_main_2cca58_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
};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
CLBlock myBlock;
{
CLPerson * person = objc_msgSend(objc_msgSend(objc_getClass("CLPerson"),
sel_registerName("alloc")
),
sel_registerName("init")); objc_msgSend(person, sel_registerName("setAge:"),
30
);
myBlock = objc_msgSend(&__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA,
person,
570425344),
sel_registerName("copy")); }}return 0;
}
Copy the code
This is the result of the ARC environment –> block on the heap –> strong pointer CLPerson *person and the underlying implementation. We noticed that the __main_block_desc_0 structure had two more eggs in it when block captured the auto variable of the object type, instead of capturing the auto variable of the primitive type
- A function pointer
copy
, that is,__main_block_copy_0()
Internally called_Block_object_assign()
- A function pointer
dispose
, that is,__main_block_dispose_0()
Internally called_Block_object_dispose()
The other thing to notice here is,ARC δΈ
CLPerson *person
Is considered to be a strong pointer, equivalent to_strong CLPerson *person
, and weak Pointers need to be expressed explicitly as__weak CLPerson *person
. By terminal commandXcrun-sdk iphoneOS clang-arch arm64-rewrite-fobjc-arc-fobjc-Runtime =ios-9.0.0 main.m -o main.cpp
As you can seeblock
Captured in theperson
Pointer to the following
For comparison, let’s take a look at the following three scenarios:
- ARC environment –> on the heap
block
— > weak pointer__weak CLPerson *person
- ARC environment –> on the stack
block
— > strong pointerCLPerson *person
- ARC environment –> on the stack
block
— > weak pointer__weak CLPerson *person
ARC environment –> block on heap –> weak pointer __weak CLPerson *person
***********************main.m*************************
#import <Foundation/Foundation.h>
#import "CLPerson.h"
typedef void(^CLBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
CLBlock myBlock;
{// The temporary scope starts
__weak CLPerson * person = [[CLPerson alloc] init];
person.age = 30;
myBlock = ^{
NSLog(@"---------%d",person.age); }; }// The temporary scope ends
NSLog(@"-- -- -- -- -- -- -- -- -- -- -- -- --");
}
NSLog(@"------main autoreleasepool end-------");
return 0;
}
Copy the code
The underlying structure of a block is as follows
Run results show the values on the heapblockUsing weak Pointers
__weak CLPerson *person
It has no effectperson
The life cycle of the object to which it points is released when it is out of the temporary scope.
ARC –> block on stack –> strong pointer CLPerson *person
***********************main.m*************************
#import <Foundation/Foundation.h>
#import "CLPerson.h"
typedef void(^CLBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
CLBlock myBlock;
{// The temporary scope starts
CLPerson * person = [[CLPerson alloc] init];
person.age = 30;
^{
NSLog(@"---------%d",person.age); }; }// The temporary scope ends
NSLog(@"-- -- -- -- -- -- -- -- -- -- -- -- --");
}
NSLog(@"------main autoreleasepool end-------");
return 0;
}
Copy the code
The underlying structure of block is as follows
The result shows that the block on the stack uses a strong pointer CLPerson *person, which does not affect the life cycle of the object to which person points, and is freed when out of temporary scope.
ARC environment –> block on stack –> weak pointer __weak CLPerson *person
***********************main.m*************************
#import <Foundation/Foundation.h>
#import "CLPerson.h"
typedef void(^CLBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
CLBlock myBlock;
{// The temporary scope starts
__weak CLPerson * person = [[CLPerson alloc] init];
person.age = 30;
^{
NSLog(@"---------%d",person.age); }; }// The temporary scope ends
NSLog(@"-- -- -- -- -- -- -- -- -- -- -- -- --");
}
NSLog(@"------main autoreleasepool end-------");
return 0;
}
Copy the code
The underlying structure of block is
The result shows that blocks on the stack use a weak pointer __weak CLPerson *person, which does not affect the life cycle of the object to which person points, and is released after the temporary scope.
If a block on the heap uses a strong pointer CLPerson *person, it will affect the lifetime of the object to which the pointer points.
__main_block_desc_0 function pointer copy and dispose. These two Pointers are actually used when a block is copied from stack space to heap space, and when a block in heap space is freed.
When a block is copied from the stack to the heap, the function __xxx_block_copy_x is called from the function pointer copy in the block
__xxx_block_copy_x(struct __main_block_impl_0*dst,
struct __main_block_impl_0*src
);
Copy the code
Notice the two arguments to this function,
- The first one
dst
Before copyingBlocks in stack space. - The second
src
After copyBlocks in heap space.
This function calls the _Block_object_assign function
_Block_object_assign((void*)&dst->person,
(void*)src->person,
3/*BLOCK_FIELD_IS_OBJECT*/
);
Copy the code
This function assigns the value of the Person pointer captured inside the DST to the Person pointer in the SRC stack block. In an ARC environment, SRC (heap block) holds objects based on the strength of person (the previous __weak and __strong modifiers). If person is decorated with __strong, the SRC (heap block) holds (strong reference) the object to which person points by making the reference count of the object to which person points +1 via [Person retain].
Dispose calls the function __xxx_block_dispose_x through its function pointer dispose when a block on the heap is about to be released
__main_block_dispose_0(struct __main_block_impl_0*src);
Copy the code
SRC is the block on the heap, which calls the _Block_object_dispose function internally
_Block_object_dispose((void*)src->person,
3/*BLOCK_FIELD_IS_OBJECT*/
);
Copy the code
In ARC, this function determines what to do with the object to which person points by taking into account the __weak/__strong nature of the person pointer inside the SRC block. If __strong is used, SRC holds the object to which person points (a strong reference). Since SRC is about to be released, it needs to be released, i.e. call [Person Release] so that the reference count of the object to which person refers is -1.
Why do I emphasize ARC environment above, because MRC does not have __weak and __strong. ARC automatically does some memory management based on the __weak/__strong flags in our code. Believe the above analysis should be able to let you understand.
If you go back to MRC, the essence of memory management remains the same, just retain and release operations, and I personally think the process will be simpler than ARC. First, a block that internally uses the auto variable (base type/object type) is in stack space and does not retain the auto variable of the object type, i.e. does not hold the object. When we call copy on a stack block, the _Block_object_assign function is eventually called in the same way as ARC, except that the retained objects captured on the heap block are retained. __weak/__strong judgment is not made, because MRC does not have any of these two little brothers. When a block on the heap is about to be released, the _Block_object_dispose function is finally called, which calls the release method on the object captured by the block, thus freeing the block on the heap from holding on to the object (strong reference).
Finally, through a few illustrations, to illustrate [Block captures the auto variable of an object typeγ
This is how block captures the auto variable of the object type.
Block portal π¦π¦π¦
Exploring the nature of blocks (1) — Basic knowledge
Explore the essence of blocks (2) — the underlying structure
Explore the essence of blocks (3) — basic type of variable capture
Explore the nature of blocks (4) — the type of Block
Exploring the nature of blocks (6) — An in-depth analysis of __blocks