Resources to prepare
Block
Introduction to the types of
Block
There are three types , Namely :GlobalBlock
、MallocBlock
、StackBlock
:
-
GlobalBlock
:-
Located in the global area
-
stay
Block
Internal cannot capture external variables , Or use only static or global variables
-
-
MallocBlock
-
Located in the heap area
-
stay
Block
Internal use of local variables orOC
attribute , And assigned to a strong reference orCopy
Decorated variable
-
-
StackBlock
-
In the stack area
-
And
MallocBlock
equally , You can use local variables or... InternallyOC
attribute . But it cannot be assigned to a strong reference orCopy
Decorated variable
-
GlobalBlock
typedef void(^KCBlock)(id data);
Copy code
overall situation Block
, Cannot capture external variables , But you can use static variables or global variables :
- Use static variables 、 Global variables 、
Block
Internally declared variables , No problem .
MallocBlock
Heap area Block
, Internal use of local variables or OC
attribute , And assigned to a strong reference or Copy
Decorated variable :
-
On the outside
a
Variables to capture ; -
Assigned to a strong reference
block
; -
among
block
Holding a heapBlock
Memory address of .
StackBlock
The stack area Block
, And heap area Block
The use of is roughly the same , The difference is that it cannot be assigned to a strong reference or Copy
Decorated variable :
- Also for external
number
Variables to capture - And heap area
Block
The difference between : Assigned to a weak referenceblock
Block
Case study
Block
Memory copy of
NSObject+Block.h
NSObject+Block.m
Then implement relevant cases in the controller :
Code reading
:
-
First step ,
weakBlock
For stack areaBlock
; -
The second step , Press
Block
The underlying source code , Customize_LGBlock
Structure . As long as the memory structure is consistent , Can beBlock
Bridge to custom objects ; -
The third step ,
Block
The essence of is structure , Assign the first address of the structure to__strong
Decorated object ; -
Step four , Put the structure's
invoke
empty , namely :Block
Function pointer of ; -
Step five , take
strongBlock
Assigned to a strong referencestrongBlock1
, And then call it .
The reason for the retreat
:
-
In the third step , Assign the first address of the structure to the object , Both point to the same memory space ;
-
In step four , Put the structure's
invoke
empty , What is modified is the same memory space ; -
In step five , take
invoke
EmptyBlock
Assign a value tostrongBlock1
, Bad address access when calling , Program flash back .
Modify the case :
First put the stack area Block
Assigned to a strong reference Block
, And then invoke
empty :
But still dodge .
The reason for the retreat :Block
The assignment of is only a shallow copy , It is equivalent to copying the pointer to the object , Generate a new pointer to the object , But the two pointers still point to the same object
therefore , terms of settlement , Must be in invoke
Before emptying , take Block
Make a deep copy :
Use lldb
, Look at two Block
stay invoke
Changes before and after setting empty :
- Deep copy is equivalent to copying objects , Create a new object . And the instance variables of each pointer type will be copied recursively , Until the two objects have no common parts .
therefore , The stack area Block
take invoke
( The pointer ) empty , Does not affect the reactor area Block
Call to .
Reference count processing of external variables
Code reading
:
-
First step ,
objc
Print after initialization1
, No problem ; -
The second step , stay
strongBlock
Print in3
, Split into two steps for analysis :1、 external
objc
Variable , Stacked areaBlock
Capture , Reference count+1
;2、 The stack area
Block
Assigned to a strong referencestrongBlock
, Put the stack areaBlock
Copy to the heap , Deep copy of the underlying layer , The reference count will also+1
. -
The third step , Assigned to a weak reference
weakBlock
, Belongs to stack areaBlock
, External onlyobjc
Variables to capture , Reference count+1
. -
Step four , Put the stack area
Block
callcopy
Method , Assign a value tomallocBlock
, Only for stack areaBlock
Made a deep copy , Reference count+1
.
In fact, the third and fourth steps , Equivalent to the decomposition of the second step , So print the results :1、3、4、5
.
Stack Block
Release
Code reading :
-
First step ,
weakBlock
Use__weak
modification , The assignment isnil
; -
The second step , Define code blocks , Implement a heap
Block
; -
The third step , Put the pile area
Block
Assigned to... Outside the code blockweakBlock
; -
Step four , After the code block is executed , call
weakBlock
.
The reason for the retreat :
-
In the third step , Heap area
Block
assignment__weak
EmbellishedweakBlock
, Equivalent to mapping relationship ; -
In step four , Contemporary code block execution completed ,
strongBlock
Because it's a heap areaBlock
, Out of the code block, it will be released . As a mappingweakBlock
, Nature will also be set tonil
. Call it now , Bad address access , Program flash back .
Modify the case :
- take
strongBlock
Use__weak
modification , You can print normally .
When strongBlock
Use __weak
After modification , Become a stack area Block
. Assign it to __weak
Embellished weakBlock
, This is still the stack area Block
. The stack area Block
The life cycle of is independent of the code block , Depending on the function stack frame , So you can print normally .
Block
Torture ⻉ To heap area
If Block
For the whole Block
, It will not be copied to the heap in any way , Even manual copy
It's no use , It's still the big picture Block
:
besides , The following four operations , The system will Block
Copy to the heap :
-
Manual
Copy
; -
Block
As return value ; -
Be strongly referenced or
Copy
modification ; -
System
API
containusingBlock
.
Block
As return value
Because the stack area Block
Once the variable field to which it belongs ends , Then the Block
Will be destroyed . stay ARC
In the environment , The compiler will automatically judge , hold Block
Automatically from the stack area copy
To heap area . for example : When Block
As the return value of a function , Definitely copy
To heap area .
System API
contain usingBlock
When Block
Is a function parameter , It needs to be manually copy
To heap area . But the system API
We don't need to deal with , such as GCD
Carried in usingBlock
Method . Other custom methods pass Block
Is a parameter , All need to be done manually copy
.
Circular reference
Compare the following two Block
Code :
-
Block1
There will be a circular reference , becauseblock
Byself
hold , andblock
Use inself
, thereforeself
Has beenblock
hold . In this case of mutual possession , There will be a circular reference . -
Block2
There will be no circular references , becauseblock
The holder of this is UIView, andself
irrelevant , There will be no mutual holding , So there is no circular reference .
The process of normal release of objects :
- When
A
holdB
,B
OfretainCount
Conduct+1
; - When
A
Triggerdealloc
when , Will giveB
Signal ,B
OfretainCount
Conduct-1
. here B OfretainCount
If0
, Will calldealloc
, Normal release .
The process of object circular reference :
-
When
A
andB
Hold each other sometimes ,A
Ofdealloc
Unable to trigger , becauseA
Have to waitB
Signal toretainCount
Conduct-1
; -
however
B
Ofdealloc
Can't trigger , becauseB
Also waiting forA
The signal of . hereA
andB
Are waiting for each other's release , Finally, a circular reference .
Avoid circular references :
-
weak-strong-dance
; -
Block
Forcibly cut off the holder ; -
Treat the holder as
Block
Parameters are passed and used .
weak-strong-dance
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
NSLog(@"%@",strongSelf.name);
};
Copy code
-
take
self
Assign a value to__weak
EmbellishedweakSelf
, hereweakSelf
Belong toself
Mapping , Point to the same memory space , alsoself
The reference count does not change ; -
stay
Block
Lieutenant generalweakSelf
Assign a value to__strong
EmbellishedstrongSelf
, avoidself
Early release causes access tonil
The situation of ; -
because
strongSelf
Is a temporary variable , stayBlock
After the scope ends , Automatically release , Therefore, there is no circular reference .
Block
Forcibly cut off the holder
__block ViewController *vc = self;
self.block = ^{
NSLog(@"%@",vc.name);
vc = nil;
};
self.block();
Copy code
-
Use
__block
Decorate the object , otherwisevc
Can't change , In other words, it cannot be set tonil
; -
stay
Block
End of use , Manually set the object tonil
. It is equivalent to manually cutting off the holding relationship , You can avoid circular references ; -
defects : This way,
Block
Must call , Otherwise, the holding relationship cannot be cut off manually ,self
andblock
Can't release , Finally, a circular reference .
Treat the holder as Block
Parameters are passed and used
self.vcBlock = ^(ViewController *vc){
NSLog(@"%@",vc.name);
};
self.vcBlock(self);
Copy code
-
take
self
As a parameter , Provide toBlock
For internal use . WhenBlock
end of execution ,vc
It will be released automatically , And then cut off the relationship with each other ,self
Will also release ; -
This way, ,
self
Whether the depends onBlock
End of execution for . IfBlock
There is deferred code in ,self
The release of will also be delayed .
Interview questions
The following cases , Whether there will be circular references ?
Case study 1
:
static ViewController *staticSelf_;
- (void)blockWeak_static {
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
}
[self blockWeak_static];
Copy code
-
There will be a circular reference ;
-
take
self
assignment__weak
Decorated object , They belong to the mapping relationship , Point to the same memory space . WhenweakSelf
Assign a global static variable ,staticSelf_
It will not be released actively during program operation , It will continue to holdself
, thereforeself
You can't release .
Case study 2
:
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@"%@", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
}
[self block_weak_strong];
Copy code
-
There will be a circular reference ;
-
stay
doWork
Inside ,strongSelf
What is held isself
. althoughstrongSelf
It's a temporary variable , But indoStudent
Is held in , Causes the reference count+1
. staydoWork
Reference count after execution-1
, butdoStudent
The holding in still exists , So there will be circular references .
Bottom cpp
analysis
Block
The essence
establish block.c
file , Write the following code :
#include "stdio.h"
int main() {
void(^block)(void) = ^{
printf("LG");
};
block();
return 0;
}
Copy code
Use xcrun
command , Generate block.cpp
file , Bottom C++ file .
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc block.c -o block.cpp
Copy code
open block.cpp
file , find main
function :
For ease of reading , Eliminate forced conversion code
int main() {
void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));
block->FuncPtr(block);
return 0;
}
Copy code
-
Only two lines of code are generated :
-
call
__main_block_impl_0
function , Pass in two parameters , Take the address and assign a valueblock
; -
call
block
OfFuncPtr
function , Pass inblock
.
-
find __main_block_impl_0
The definition of :
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__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;
}
}
Copy code
Parameters 1
1static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("LG");
}
Copy code
Parameters 2
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)};
Copy code
-
It's not hard to see. ,
Block
The essence of is structure ,main
The constructor of the structure is called in the function . -
The structure contains two member variables :
-
__block_impl
Structure typeimpl
; -
__main_block_desc_0
Structure pointer typeDesc
.
-
-
Generated in the constructor
flags
be equal to0
The default value of , assignmentimpl
OfFlags
. -
Parameters
fp
byBlock
Function pointer to the code block , assignmentimpl
OfFuncPtr
. -
Parameters
desc
Assign to member variableDesc
. -
therefore
main
Function , Codeblock->FuncPtr(block)
It's rightBlock
To call . -
thus it can be seen , When
Block
Only define and do not call execution , Not triggerBlock
Code block in .
Capture external variables
The code is as follows :
Generate block.cpp
file , find main
function :
int main(){
int a = 8;
void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a);
block->FuncPtr(block);
return 0;
}
Copy code
-
The code has changed , Before
__main_block_impl_0
The arguments to the function become three ; -
The third parameter added is the external variable
a
.
find __main_block_impl_0
The definition of :
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
Copy code
Parameters 1
:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
printf("LG - %d",a);
}
Copy code
Parameters 2
:
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)};
Copy code
-
The member variables in the structure have also changed , When capturing external variables , Inside the structure , Corresponding member variables will be generated to store ;
-
Member variables
a
Assignment through the constructor of the structure :a(_a)
; -
main
Call in functionBolck
, Due to the capture of external variables , At this time, theFuncPtr
Mediumblock
Play a role :-
block
Pointer to its own structure , takeblock
Member variables ina
Assign to a temporary variablea
, Then print it ; -
Temporary variable
a
and__cself->a
The value of is the same , But the address is different ; -
because
a
Yes value copy ,Bolck
Code block cannot be used fora
Change the value of , It will cause code ambiguity of the compiler . therefore , At this timea
Is read-only .
-
-
Capture external variables and assign strong reference variables , It was supposed to be a heap area
Block
, But in the structureimpl
Ofisa
The assignment is&_NSConcreteStackBlock
, Mark as stack areaBlock
. Because at compile time , Unable to open up memory space , So mark it asStackBlock
. At run time , Will, as the case may beBlock
Copy to the heap , Then generateMallocBlock
.
__block
The role of
The code is as follows :
#include "stdio.h"
int main() {
__block int a = 18;
void(^block)(void) = ^{
a++;
printf("LG - %d",a);
};
block();
return 0;
}
Copy code
Generate block.cpp
file , find main
function :
int main() {
__Block_byref_a_0 a = {
0,
(__Block_byref_a_0 *)&a,
0,
sizeof(__Block_byref_a_0),
18
};
void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344);
block->FuncPtr(block);
return 0;
}
Copy code
int
typea
, Corresponding generation__Block_byref_a_0
Structure .Member variables 2
, For the structurea
Address fetch , To structure pointer .
find __Block_byref_a_0
Definition :
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
Copy code
-
among
__forwarding
What's stored isa
The address of the structure ; -
The last member variable
a
Storage18
Value ; -
The structure stores its own address and value .
find __main_block_impl_0
The definition of :
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
Copy code
Parameters 1
:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a)++;
printf("LG - %d",(a->__forwarding->a));
}
Copy code
Parameters 2
:
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};
// Parameters 2 The use of copy and dispose function
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
Copy code
-
local variable
a
and__cself->a
The pointer address is the same , Once its value changes , It is equivalent to modifying the value of external variables ; -
No,
__block
modification , Belongs to value copy , That's deep copy . The copied value cannot be changed , They point to different memory spaces ; -
Use
__block
modification , Belongs to address copy , It's a shallow copy . The generated object points to the same piece of memory space , Internal modification is equivalent to modification of external variables .
Assembly analysis
Process analysis
build App
project , Write the following code :
- (void)viewDidLoad {
[super viewDidLoad];
__block NSObject *objc = [NSObject alloc];
void (^block)(void) = ^{
NSLog(@"LG_Block %@ ",objc);
};
block();
}
Copy code
in the light of block
Set breakpoints according to the definition of , Run the project , Look at assembly code :
In the project , Set up objc_retainBlock
Symbolic breakpoints :
- come from
libobjc.A.dylib
frame .
open objc4-818.2
Source code , find objc_retainBlock
function :
id objc_retainBlock(id x) {
return (id)_Block_copy(x);
}
Copy code
- call
_Block_copy
function , howeverobjc
Its implementation cannot be found in the source code
In the project , Set up _Block_copy
Symbolic breakpoints :
come from libsystem_blocks.dylib
frame , However, the framework is not open source yet , Can be in libclosure
View in alternative project .
Block
structure
adopt cpp
analysis ,Block
The essence of is structure . stay libclosure-79
Source code , Came to _Block_copy
function , Can find Block
The real type of :Block_layout
.
find Block_layout
The definition of :
-
isa
: MarkBlock
Class of type ; -
flags
: identifier , Pressbit
Who saidBlock
Additional information for , Be similar toisa
Bit fields in ; -
reserved
: Reserved location ; -
invoke
: A function pointer , Point toBlock
The calling address of the implementation ; -
descriptor
: Additional information , for example : Number of reserved variables stored 、Block
Size 、 Conductcopy
ordispose
Function pointer of .
find flags
Mark :
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
BLOCK_INLINE_LAYOUT_STRING = (1 << 21), // compiler
#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
BLOCK_SMALL_DESCRIPTOR = (1 << 22), // compiler
#endif
BLOCK_IS_NOESCAPE = (1 << 23), // compiler
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
};
Copy code
-
BLOCK_DEALLOCATING
: Release the tag , It is commonly used inBLOCK_BYREF_NEEDS_FREE
Do bit and operation , Simultaneous interpretingflags
, Tell me thatBlock
Releasable ; -
BLOCK_REFCOUNT_MASK
: Store the value of the reference count , Is an optional parameter ; -
BLOCK_NEEDS_FREE
:low 16 position
Whether it is valid or not , According to it, the program determines whether to increase or decrease the value of the reference count digit ; -
BLOCK_HAS_COPY_DISPOSE
: Do you have a copy helper function , Used to copy to the heap , decisionblock_description_2
; -
BLOCK_HAS_CTOR
: Owned or notBlock
OfC++
Destructor ; -
BLOCK_IS_GC
: Mark whether there is garbage collection ,OSX
; -
BLOCK_IS_GLOBAL
: Whether the flag is globalBlock
; -
BLOCK_USE_STRET
: AndBLOCK_HAS_SIGNATURE
relative , Judge if it's currentBlock
Have a signature , be used forruntime
Call... Dynamically when ; -
BLOCK_HAS_SIGNATURE
: Is there a signature ; -
BLOCK_HAS_EXTENDED_LAYOUT
: Whether there is expansion , decisionblock_description_3
.
Runtime Copy
Get into _Block_copy
Assembly code of function , Read x0
Register value , And print it :
- Get into
_Block_copy
function , At presentBlock
Marked asStackBlock
.
Run directly to the end of the function , Read the return value x0
register , And print it :
- Program runtime , At present
Block
accord withMallocBlock
Conditions , after_Block_copy
function , WillBlock
Copy to heap .
Block
call
_Block_copy
The function is finished , go back to viewDidLoad
Method , Keep going Block
Call to :
-
ldr x8, [x0, #0x10]
:x0
For the currentBlock
, Readx0 + 16 byte
Value , Assign a value tox8
; -
Block_layout
In the structure , First address offset16 byte
, It's like skippingisa
、flags
andreserved
, Readinvoke
Function address , Assign a value tox8
;
-
blr x8
: Jump toinvoke
Function address , amount toBlock
Call to .
descriptor
When _Block_copy
The function is finished , Print current Block
by MallocBlock
:
- It also prints out
signature
、copy
、dispose
Data such as .
descriptor
type
signature
、copy
、dispose
Data such as , Stored in Block_layout
Structure of the descriptor
in
descriptor
There are three types :
-
Block_descriptor_1
-
Block_descriptor_2
-
Block_descriptor_3
among Block_descriptor_1
There must be ,Block_descriptor_2
and Block_descriptor_3
Optional
find descriptor
The definition of :
Block_descriptor_1
: Store reserved fields andBlock
size
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};
Copy code
Block_descriptor_2
: Storagecopy
anddispose
Function pointer of
#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
// requires BLOCK_HAS_COPY_DISPOSE
BlockCopyFunction copy;
BlockDisposeFunction dispose;
};
Copy code
Block_descriptor_3
: Storagesignature
Signature andlayout
#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
// requires BLOCK_HAS_SIGNATURE
const char *signature;
const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
Copy code
Block_descriptor_1
The read :
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock) {
return aBlock->descriptor;
}
Copy code
- because
Block_descriptor_1
There must be , Directly throughBlock
Ofdescriptor
Read .
Block_descriptor_2
The read :
static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock) {
if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
uint8_t *desc = (uint8_t *)aBlock->descriptor;
desc += sizeof(struct Block_descriptor_1);
return (struct Block_descriptor_2 *)desc;
}
Copy code
-
By bit and operation , Judge
Block_descriptor_2
non-existent , returnNULL
; -
otherwise , Read
Block_descriptor_1
The first address , Offset its own size , namely :Block_descriptor_2
The first address ; -
Forced to
Block_descriptor_2
The structure pointer of returns .
Block_descriptor_3
The read :
static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock) {
if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
uint8_t *desc = (uint8_t *)aBlock->descriptor;
desc += sizeof(struct Block_descriptor_1);
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
desc += sizeof(struct Block_descriptor_2);
}
return (struct Block_descriptor_3 *)desc;
}
Copy code
-
By bit and operation , Judge
Block_descriptor_3
non-existent , returnNULL
; -
otherwise , Read
Block_descriptor_1
The first address , Offset its own size , namely :Block_descriptor_2
The first address ; -
By bit and operation , Judge
Block_descriptor_2
Whether there is :-
There is , Re migration
Block_descriptor_2
size , namely :Block_descriptor_3
The first address ; -
non-existent , Forced to
Block_descriptor_3
The structure pointer of returns .
-
because Block_descriptor_2
and Block_descriptor_3
The memory structure is the same , So offset Block_descriptor_1
Address after , Can be forced to 2
, It can be forced into 3
.
lldb
verification descriptor
adopt flags
Carry out bit and operation , It can be learned that Block_descriptor_2
and Block_descriptor_3
Whether there is .
Use x/8g
command , Output Block
Memory structure :
(lldb) x/8g 0x283aaa430
0x283aaa430: 0x00000001de1c0880 0x00000000c3000002
0x283aaa440: 0x00000001021422ac 0x0000000102144018
0x283aaa450: 0x0000000283aaa400 0x0000000000000000
0x283aaa460: 0x000021a1de1c6221 0x0000000000000000
Copy code
0xc3000002
:Block
Offlags
identifier .
verification Block_descriptor_2
:
//BLOCK_HAS_COPY_DISPOSE = (1 << 25)
(lldb) p/x 1 << 25
(int) $7 = 0x02000000
(lldb) p/x (0xc3000002 & 0x02000000)
(unsigned int) $8 = 0x02000000
Copy code
- The result is not
0
, proveBlock_descriptor_2
There is .
verification Block_descriptor_3
:
//BLOCK_HAS_SIGNATURE = (1 << 30)
(lldb) p/x 1 << 30
(int) $10 = 0x40000000
(lldb) p/x (0xc3000002 & 0x40000000)
(unsigned int) $16 = 0x40000000
Copy code
- The result is not
0
, proveBlock_descriptor_3
There is .
Block
Signature
lldb
Verify the signature
Use x/8g
command , Output Block
Memory structure :
(lldb) x/8g 0x283aaa430
0x283aaa430: 0x00000001de1c0880 0x00000000c3000002
0x283aaa440: 0x00000001021422ac 0x0000000102144018
0x283aaa450: 0x0000000283aaa400 0x0000000000000000
0x283aaa460: 0x000021a1de1c6221 0x0000000000000000
Copy code
- First address translation
24 byte
,0x0000000102144018
Namelydescriptor
Structure pointer of .
Use x/8g
command , Output descriptor
Memory structure :
(lldb) x/8g 0x0000000102144018
0x102144018: 0x0000000000000000 0x0000000000000028
0x102144028: 0x00000001021422e0 0x00000001021422f0
0x102144038: 0x00000001021433e3 0x0000000000000010
0x102144048: 0x00000001de78a280 0x00000000000007c8
Copy code
-
0x00000001021422e0
:copy
A function pointer ; -
0x00000001021422f0
:dispose
A function pointer ; -
0x00000001021433e3
:signature
Signature .
Yes 0x00000001021433e3
Perform forced rotation output :
(lldb) po (char *)0x00000001021433e3
"[email protected]?0"
Copy code
Signature means
For signatures [email protected]?0
The explanation of :
-
v
: return typeviod
-
8
: Method8 byte
-
@?
:Parameters 0
type -
0
:Parameters 0
Starting position , from0 byte
Start
Signature details , have access to NSMethodSignature
Of signatureWithObjCTypes
Method output :
(lldb) po [NSMethodSignature signatureWithObjCTypes:"[email protected]?0"] <NSMethodSignature: 0xbb2001894a750adf>
number of arguments = 1
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
Copy code
-
number of arguments = 1
: Means to pass in a parameter ; -
is special struct return? NO
: no return value ; -
return value
: Return value :-
type encoding (v) 'v'
:void
Abbreviation for type ; -
memory {offset = 0, size = 0}
: No return value , thereforesize
by0
.
-
-
argument 0
:Parameters 0
; -
type encoding (@) '@?'
: among@
byid
type ,?
Unknown type ; -
flags {isObject, isBlock}
: That isObject
type , It's alsoBlock
type ; -
memory {offset = 0, size = 8}
:Parameters 0
from0 byte
Start , Occupy8 byte
.
Source code analysis
Use alternative works libclosure
Source code analysis :
When Block
Capture using __block
Decorated object , The bottom layer triggers Block
Three layer copy of .
_Block_copy
Get into _Block_copy
function :
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
// If arg by NULL, Go straight back to NULL
if (!arg) return NULL;
// The following would be better done as a switch statement
// Forced to Block_layout type
aBlock = (struct Block_layout *)arg;
// If it's already on the pile
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
// Just add... To the reference count 1
latching_incr_int(&aBlock->flags);
return aBlock;
}
// If block In the global area , No reference count , You don't have to copy , Go straight back to block In itself
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
else {// Stack - Pile up ( Compile time )
// Its a stack block. Make a copy.
// block Now on the stack , Now you need to copy it to the heap
// Reopen a piece of the heap and aBlock Same size of memory
size_t size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);
// Failed to open up , return NULL
if (!result) return NULL;
// take aBlock All the data on the memory is copied to the newly opened result On
memmove(result, aBlock, size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;
#if __has_feature(ptrauth_signed_block_descriptors)
if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
uintptr_t oldDesc = ptrauth_blend_discriminator( &aBlock->descriptor, _Block_descriptor_ptrauth_discriminator);
uintptr_t newDesc = ptrauth_blend_discriminator( &result->descriptor, _Block_descriptor_ptrauth_discriminator);
result->descriptor = ptrauth_auth_and_resign(aBlock->descriptor, ptrauth_key_asda, oldDesc, ptrauth_key_asda, newDesc);
}
#endif
#endif
// reset refcount
// take flags Medium BLOCK_REFCOUNT_MASK and BLOCK_DEALLOCATING Part of the bits are cleared to 0
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed
// take result The tag bit is on the heap , Manual release required ; And the reference count is initialized to 1
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
// copy Method will be called to copy member variables
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
// isa Point to _NSConcreteMallocBlock
result->isa = _NSConcreteMallocBlock;
return result;
}
}
Copy code
-
_Block_copy
function , be responsible forBlock
A copy of the object itself , Copy from stack to heap ; -
Parameters
arg
NamelyBlock_layout
object ; -
If it was on the pile , The reference count
+1
; -
If
Block
In the global area , No reference count , You don't have to copy , Go straight back toBlock
In itself ; -
If it was on the stack , Will be copied to the heap , The reference count is initialized to
1
, And will call_Block_call_copy_helper
Method ( If it exists ); -
The return value is after copying
Block
The address of .
_Block_object_assign
cpp
analysis
open cpp
file , Find the statement Block
The second parameter of ,__main_block_desc_0_DATA
Definition of structure :
//void(*block)(void) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344);
__main_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0),
__main_block_copy_0,
__main_block_dispose_0
};
Copy code
-
__main_block_copy_0
:Block_descriptor_2
Mediumcopy
; -
__main_block_dispose_0
:Block_descriptor_2
Mediumdispose
.
find __main_block_copy_0
The definition of :
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
Copy code
- call
_Block_object_assign
function .
lldb
analysis
Use x/8g
command , Output Block
Memory structure :
(lldb) x/8g 0x000000028167eca0
0x28167eca0: 0x00000001de1c0880 0x00000000c3000002
0x28167ecb0: 0x0000000104f1a2ac 0x0000000104f1c018
0x28167ecc0: 0x000000028167ec70 0x0000000000000000
0x28167ecd0: 0x000021a1de1c6221 0x0000000000000000
Copy code
Use x/8g
command , Output descriptor
Memory structure :
(lldb) x/8g 0x0000000104f1c018
0x104f1c018: 0x0000000000000000 0x0000000000000028
0x104f1c028: 0x0000000104f1a2e0 0x0000000104f1a2f0
0x104f1c038: 0x0000000104f1b3e3 0x0000000000000010
0x104f1c048: 0x00000001de78a280 0x00000000000007c8
Copy code
0x0000000104f1a2e0
:copy
A function pointer
Use dis -s
Read assembly code :
(lldb) dis -s 0x0000000104f1a2e0
004-Block Structure and signature `__copy_helper_block_e8_32r:
0x104f1a2e0 <+0>: add x0, x0, #0x20 ; =0x20
0x104f1a2e4 <+4>: ldr x1, [x1, #0x20]
0x104f1a2e8 <+8>: mov w2, #0x8
0x104f1a2ec <+12>: b 0x104f1a484 ; symbol stub for: _Block_object_assign
Copy code
- call
_Block_object_assign
function .
Timing of invocation
stay _Block_copy
Function , call _Block_call_copy_helper
function
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock) {
if (auto *pFn = _Block_get_copy_function(aBlock)) pFn(result, aBlock);
}
Copy code
Get into _Block_get_copy_function
function
static inline __typeof__(void (*)(void *, const void *)) _Block_get_copy_function(struct Block_layout *aBlock) {
if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE))
return NULL;
void *desc = _Block_get_descriptor(aBlock);
#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
struct Block_descriptor_small *bds = (struct Block_descriptor_small *)desc;
return _Block_get_relative_function_pointer( bds->copy, void (*)(void *, const void *));
}
#endif
struct Block_descriptor_2 *bd2 = (struct Block_descriptor_2 *)((unsigned char *)desc + sizeof(struct Block_descriptor_1));
return _Block_get_copy_fn(bd2);
}
Copy code
-
If there is
copy
anddispose
, Get... From memoryBlock_descriptor_2
Structure pointer of ; -
call
_Block_get_copy_fn
function , Pass inbd2
.
Get into _Block_get_copy_fn
function :
static inline __typeof__(void (*)(void *, const void *)) _Block_get_copy_fn(struct Block_descriptor_2 *desc) {
return (void (*)(void *, const void *))_Block_get_function_pointer(desc->copy);
}
Copy code
- After processing, return to
Block_descriptor_2
Under thecopy
function .
Final , go back to _Block_call_copy_helper
function , take copy
Function address assignment pFn
, And then through pFn(result, aBlock)
Call it .
Source code analysis
In the source code , find Block
Capture the type of external variables
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.
};
Copy code
-
BLOCK_FIELD_IS_OBJECT
: Common object types ; -
BLOCK_FIELD_IS_BLOCK
:Block
Type as a variable ; -
BLOCK_FIELD_IS_BYREF
: Use__block
Decorated variable ; -
BLOCK_FIELD_IS_WEAK
:weak
Weakly referenced variables ; -
BLOCK_BYREF_CALLER
: The returned calling object , Handleblock_byref
An extra token will be added to the internal object memory , coordinationflags
Use it together .
Get into _Block_object_assign
function :
// When block and byref When you want to hold an object , Their copy helper Function will call this function to complete assignment
// Parameters destAddr It's actually a secondary pointer , Pointer to the real target
void _Block_object_assign(void *destArg, const void *object, const int flags) {
const void **dest = (const void **)destArg;
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
case BLOCK_FIELD_IS_OBJECT:
/*******
id object = ...;
[^{ object; } copy];
********/
// _Block_retain_object_default = fn (arc)
// Default to do nothing , But in _Block_use_RR() Will be Objc runtime perhaps CoreFoundation Set up retain function
// among , Maybe with runtime Make connections , Operation object reference count or something
// It can be understood as handing over to the system ARC Handle
_Block_retain_object(object);
// send dest The target pointer to points to
object *dest = object;
break;
case BLOCK_FIELD_IS_BLOCK:
/*******
void (^object)(void) = ...;
[^{ object; } copy];
********/
// send dest Point to copy to heap object
*dest = _Block_copy(object);
break;
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
/*******
// copy the onstack __block container to the heap
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__block ... x;
__weak __block ... x;
[^{ x; } copy];
********/
// send dest Point to the... Copied to the heap byref
*dest = _Block_byref_copy(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
/*******
// copy the actual field held in the __block container
// Note this is MRC unretained __block only.
// ARC retained __block is handled by the copy helper directly.
__block id object;
__block void (^object)(void);
[^{ object; } copy];
********/
// send dest The target pointer to points to object
*dest = object;
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
/*******
// copy the actual field held in the __block container
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__weak __block id object;
__weak __block void (^object)(void);
[^{ object; } copy];
********/
// send dest The target pointer to points to object
*dest = object;
break;
default:
break;
}
}
Copy code
-
Common object types , Give it to the system
ARC
Handle , senddest
The target pointer to points toobject
; -
Block
Type as a variable , call_Block_copy
function , senddest
Point to copy to heapobject
; -
Use
__block
Decorated variable , call_Block_byref_copy
function , senddest
Point to the... Copied to the heapbyref
.
_Block_byref_copy
Get into _Block_byref_copy
function :
// 1. If byref It was on the pile , Copy it to the heap , Copies include Block_byref、Block_byref_2、Block_byref_3
// By __weak Embellished byref Will be modified isa by _NSConcreteWeakBlockVariable
// original byref Of forwarding It will also point to... On the heap byref;
// 2. If byref Already on the pile , Just increase the reference count by one .
static struct Block_byref *_Block_byref_copy(const void *arg) {
// arg Forced to Block_byref * type
struct Block_byref *src = (struct Block_byref *)arg;
// The reference count is equal to 0
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack
// For the new byref Allocating memory in the heap
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
copy->isa = NULL;
// byref value 4 is logical refcount of 2: one for caller, one for stack
// new byref Of flags It is marked on the heap , And the reference count is 2.
// Why 2 Well ? The note says non-GC one for caller, one for stack
// one for caller Well understood. , that one for stack Why ?
// Look at a line in the following code src->forwarding = copy.src Of forwarding It also points to copy, It is equivalent to quoting
copy copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
// Pile it up byref Of forwarding Point to your
copy->forwarding = copy; // patch heap copy to point to itself
// The original stack byref Of forwarding Now it also points to the... On the heap byref
src->forwarding = copy; // patch stack to point to heap copy
// Copy size
copy->size = src->size;
// If src Yes copy/dispose helper
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
// obtain src and copy Of Block_byref_2
struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
// copy Of copy/dispose helper Also with the src bring into correspondence with
// Because it's a function pointer , Probably not on the stack , So don't worry about being destroyed
copy2->byref_keep = src2->byref_keep;
copy2->byref_destroy = src2->byref_destroy;
// If src There is an extended layout , Also copy the extended layout
if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
// No will layout Copy the string to the heap , Because it's const Constant , Not on the stack
copy3->layout = src3->layout;
}
// call copy helper, because src and copy Of copy helper It's the same , So anyone can use it , The same function is called
// Initiate layer 3 copy
(*src2->byref_keep)(copy, src);
} else {
// Bitwise copy.
// This copy includes Block_byref_3, if any.
// If src No, copy/dispose helper
// take Block_byref The following data is copied to copy in , Must include Block_byref_3
memmove(copy+1, src+1, src->size - sizeof(*src));
}
}
// already copied to heap
// src Already on the pile , Just add... To the reference count 1
else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
return src->forwarding;
}
Copy code
-
Yes
Block_byref
Copy , It belongs to the second layer of the three-layer copy ; -
If the reference count is
0
, For the newbyref
Allocating memory in the heap :-
Pile up
byref
Offorwarding
Point to your ; -
Put the... On the original stack
byref
Offorwarding
It also points to... On the heapbyref
; -
from
byref_keep
launchBlock
Layer 3 copy of .
-
-
If it's already on the pile , Just count the references
+1
;
byref_keep
analysis
find byref_keep
The definition of :
struct Block_byref {
void *isa;
struct Block_byref *forwarding;
volatile int32_t flags; // contains ref count
uint32_t size;
};
struct Block_byref_2 {
// requires BLOCK_BYREF_HAS_COPY_DISPOSE
BlockByrefKeepFunction byref_keep;
BlockByrefDestroyFunction byref_destroy;
};
struct Block_byref_3 {
// requires BLOCK_BYREF_LAYOUT_EXTENDED
const char *layout;
};
Copy code
Corresponding cpp
The file to view :
struct __Block_byref_objc_0 {
void *__isa;
__Block_byref_objc_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *objc;
};
Copy code
- In the source
byref_keep
, Correspondingcpp
Medium__Block_byref_id_object_copy
.
find __Block_byref_id_object_copy
Assignment :
__Block_byref_objc_0 objc = {
(void*)0,
(__Block_byref_objc_0 *)&objc,
33554432,
sizeof(__Block_byref_objc_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"))
};
Copy code
- Pass in
__Block_byref_id_object_copy_131
.
find __Block_byref_id_object_copy_131
The definition of :
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
Copy code
View the passed in parameters according to the source code :
(*src2->byref_keep)(copy, src);
Copy code
- Passed into the heap
Block_byref
, Correspondingcpp
Medium__Block_byref_objc_0
Structure .
byref_keep
Conclusion
call byref_keep
, The bottom layer calls again _Block_object_assign
function
(char*)dst + 40
Parameters , Structure memory translation , What's coming in is objc
Instance object
Get into _Block_object_assign
function , Hit normal object type copy
Logic
Copy objects to heap , complete Block
Layer 3 copy of
Block
Conclusion of three-layer copy
When Block
Capture using __block
Decorated object , The bottom layer triggers Block
Three layer copy of
-
【 first floor 】
_Block_copy
function , be responsible forBlock
A copy of the object itself , Copy from stack to heap-
adopt
_Block_copy
→_Block_call_copy_helper
, call_Block_object_assign
function -
Pass in
Block_byref
Structure pointer , The type isBLOCK_FIELD_IS_BYREF
, call_Block_byref_copy
function
-
-
【 The second floor 】
_Block_byref_copy
function , takeBlock_byref
Copy to the heap- adopt
byref_keep
function , call_Block_object_assign
function , Pass inobjc
Instance object
- adopt
-
【 The third level 】
_Block_object_assign
function , Copy the captured external variables to the heap
Block
Release
If Block
On the pile , Need to carry out release
. In the global area and stack area Block
, No need release
.
_Block_release
Get into _Block_release
function :
// block On the pile , That's what we need release, In the global area and stack area, you don't need release.
// Reduce the reference count first 1, If the reference count drops to 0, will block The destruction
void _Block_release(const void *arg) {
struct Block_layout *aBlock = (struct Block_layout *)arg;
// If block == nil
if (!aBlock) return;
// If block In the global area
if (aBlock->flags & BLOCK_IS_GLOBAL) return;
// block Not on the pile
if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;
// Quote count minus 1, If the reference count drops to 0, Returns the true, Express block Need to be destroyed
if (latching_decr_int_should_deallocate(&aBlock->flags)) {
// call block Of dispose helper,dispose helper Methods do things like destroy byref Wait for the operation
_Block_call_dispose_helper(aBlock);
// _Block_destructInstance What also not stem , The function body is empty
_Block_destructInstance(aBlock); free(aBlock);
}
}
Copy code
- and
_Block_copy
be similar , adopt_Block_call_dispose_helper
function , call_Block_object_dispose
function .
_Block_object_dispose
Get into _Block_object_dispose
function :
// When block and byref want dispose Object time , Their dispose helper Will call this function
void _Block_object_dispose(const void *object, const int flags) {
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
// If it is byref
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
// get rid of the __block data structure held in a Block
// Yes byref Object to do release operation
_Block_byref_release(object);
break;
case BLOCK_FIELD_IS_BLOCK:
// Yes block do release operation
_Block_release(object);
break;
case BLOCK_FIELD_IS_OBJECT:
// Default to do nothing , But in _Block_use_RR() May be Objc runtime perhaps CoreFoundation Set up a release function , It may involve runtime Reference count of
_Block_release_object(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
break;
default:
break;
}
}
Copy code
-
Common object types , Give it to the system
ARC
Handle ; -
Block
Type as a variable , call_Block_release
function ; -
Use
__block
Decorated variable , call_Block_byref_release
function , Yesbyref
Object to dorelease
operation .
_Block_byref_release
Get into _Block_byref_release
function :
// Yes byref Object to do release operation ,
// On the pile byref need release, The on the stack doesn't need release,
// release Is the reference count minus 1, If the reference count drops to 0, will byref Destruction of objects
static void _Block_byref_release(const void *arg) {
struct Block_byref *byref = (struct Block_byref *)arg;
// dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
// Get the real point byref, If byref Has been copied by the heap , Then the acquisition is on the heap byref, Otherwise it's on the stack , The on the stack doesn't need release, There is no reference count
byref = byref->forwarding;
// byref Copied to the heap , need release
if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
// Get reference count
int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
os_assert(refcount);
// Quote count minus 1, If the reference count drops to 0, Returns the true, Express byref Need to be destroyed
if (latching_decr_int_should_deallocate(&byref->flags)) {
if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1);
// dispose helper Hidden in the Block_byref_2 in
(*byref2->byref_destroy)(byref);
}
free(byref);
}
}
}
Copy code
- and
_Block_byref_copy
be similar , frombyref_destroy
The of the initiating objectrelease
.
Corresponding cpp
Code :
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
Copy code
-
call
_Block_object_dispose
function , Pass inobjc
Instance object . -
Get into
_Block_object_dispose
function , Hit normal object typerelease
Logic .