Exploring the underlying principles of IOS - block

Smile a centimeter every day 2021-09-15 08:26:39

Resources to prepare

Block Introduction to the types of

Block There are three types , Namely :GlobalBlockMallocBlockStackBlock

  • 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 or OC attribute , And assigned to a strong reference or Copy Decorated variable

  • StackBlock

    • In the stack area

    • And MallocBlock equally , You can use local variables or... Internally OC attribute . But it cannot be assigned to a strong reference or Copy 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 :

C8FC72A9-1E10-4F4D-A7DA-DCDB088343CE.png

  • 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 :

684268D0-AB31-41EF-81F5-541DE9E8C4F9.png

  • On the outside a Variables to capture ;

  • Assigned to a strong reference block;

  • among block Holding a heap Block 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 :

4CC6566C-A108-4AB3-A9B3-181331341B9A.png

  • Also for external number Variables to capture
  • And heap area Block The difference between : Assigned to a weak reference block

Block Case study

Block Memory copy of

  • NSObject+Block.h

7BD4AE2E-A343-4A4F-8ED6-F74D5F73FAE1.png 4196F930-9B0A-44AD-A069-6F78213BD2E0.png

  • NSObject+Block.m

236FC095-C511-404C-8BC0-1CAAD8D4986A.png

Then implement relevant cases in the controller :

87DEAD4F-F330-4F9C-BDE8-9FE20FC121FC.png Code reading

  • First step ,weakBlock For stack area Block;

  • The second step , Press Block The underlying source code , Customize _LGBlock Structure . As long as the memory structure is consistent , Can be Block 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 reference strongBlock1, 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 Empty Block Assign a value to strongBlock1, 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 :

1DC0772C-B7A2-4BD8-8AAE-CCE2664D4FA9.png

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 :

4BE3B115-8539-4BD6-9B48-99D660D9A6E8.png

Use lldb, Look at two Block stay invoke Changes before and after setting empty :

363E398C-0693-4933-8DF8-9D05AC15BE8B.png

  • 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

E97F27B1-66C6-4D46-A619-7EACBB1BC87D.png

Code reading

  • First step ,objc Print after initialization 1, No problem ;

  • The second step , stay strongBlock Print in 3, Split into two steps for analysis :

    1、 external objc Variable , Stacked area Block Capture , Reference count +1;

    2、 The stack area Block Assigned to a strong reference strongBlock, Put the stack area Block 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 area Block, External only objc Variables to capture , Reference count +1.

  • Step four , Put the stack area Block call copy Method , Assign a value to mallocBlock, Only for stack area Block 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

94ADE33D-5FD5-4F7E-89F1-5BEDEC439FF1.png

Code reading :

  • First step ,weakBlock Use __weak modification , The assignment is nil;

  • The second step , Define code blocks , Implement a heap Block;

  • The third step , Put the pile area Block Assigned to... Outside the code block weakBlock;

  • Step four , After the code block is executed , call weakBlock.

The reason for the retreat :

  • In the third step , Heap area Block assignment __weak Embellished weakBlock, Equivalent to mapping relationship ;

  • In step four , Contemporary code block execution completed ,strongBlock Because it's a heap area Block, Out of the code block, it will be released . As a mapping weakBlock, Nature will also be set to nil. Call it now , Bad address access , Program flash back .

Modify the case

7B1140F5-BA72-49E9-B9B2-C1832F0E6818.png

  • 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

E2A34EE9-E504-483B-906B-6B917E174569.png

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 contain usingBlock.

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 :

59AEC1D4-CD92-4708-B00E-DFA8FDED8DDB.png

  • Block1 There will be a circular reference , because block By self hold , and block Use in self, therefore self Has been block hold . In this case of mutual possession , There will be a circular reference .

  • Block2 There will be no circular references , because block The holder of this is UIView, and self irrelevant , There will be no mutual holding , So there is no circular reference .

The process of normal release of objects :

image-1.png

  • When A hold B,B Of retainCount Conduct +1;
  • When A Trigger dealloc when , Will give B Signal ,B Of retainCount Conduct -1. here B Of retainCount If 0, Will call dealloc, Normal release .

The process of object circular reference :

image.png

  • When A and B Hold each other sometimes ,A Of dealloc Unable to trigger , because A Have to wait B Signal to retainCount Conduct -1;

  • however B Of dealloc Can't trigger , because B Also waiting for A The signal of . here A and B 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 Embellished weakSelf, here weakSelf Belong to self Mapping , Point to the same memory space , also self The reference count does not change ;

  • stay Block Lieutenant general weakSelf Assign a value to __strong Embellished strongSelf, avoid self Early release causes access to nil The situation of ;

  • because strongSelf Is a temporary variable , stay Block 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 , otherwise vc Can't change , In other words, it cannot be set to nil;

  • stay Block End of use , Manually set the object to nil. 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 and block 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 to Block For internal use . When Block 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 on Block End of execution for . If Block 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 . When weakSelf Assign a global static variable ,staticSelf_ It will not be released actively during program operation , It will continue to hold self, therefore self 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 is self. although strongSelf It's a temporary variable , But in doStudent Is held in , Causes the reference count +1. stay doWork Reference count after execution -1, but doStudent 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 :

BAF60810-574D-4050-95F5-39C13A1C0852.png

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 value block;

    • call block Of FuncPtr function , Pass in block.

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 type impl;

    • __main_block_desc_0 Structure pointer type Desc.

  • Generated in the constructor flags be equal to 0 The default value of , assignment impl Of Flags.

  • Parameters fp by Block Function pointer to the code block , assignment impl Of FuncPtr.

  • Parameters desc Assign to member variable Desc.

  • therefore main Function , Code block->FuncPtr(block) It's right Block To call .

  • thus it can be seen , When Block Only define and do not call execution , Not trigger Block Code block in .

Capture external variables

The code is as follows :

9B2A8F12-B1A0-4080-A1FB-1C3059B62347.png

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 function Bolck, Due to the capture of external variables , At this time, the FuncPtr Medium block Play a role :

    • block Pointer to its own structure , take block Member variables in a Assign to a temporary variable a, 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 for a Change the value of , It will cause code ambiguity of the compiler . therefore , At this time a Is read-only .

  • Capture external variables and assign strong reference variables , It was supposed to be a heap area Block, But in the structure impl Of isa The assignment is &_NSConcreteStackBlock, Mark as stack area Block. Because at compile time , Unable to open up memory space , So mark it as StackBlock. At run time , Will, as the case may be Block Copy to the heap , Then generate MallocBlock.

__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 type a, Corresponding generation __Block_byref_a_0 Structure . Member variables 2, For the structure a 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 is a The address of the structure ;

  • The last member variable a Storage 18 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 :

AB820AF2-7231-47FE-99B4-7B06E297E4DE.png

In the project , Set up objc_retainBlock Symbolic breakpoints :

709691C6-0647-435D-AEAD-24616090FFE7.png

  • 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 , however objc Its implementation cannot be found in the source code

In the project , Set up _Block_copy Symbolic breakpoints :

21770810-2D5B-4CD8-AF46-08A8D05283F7.png

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 :

C2D31780-873A-4495-959E-D320D3233CCC.png

  • isa: Mark Block Class of type ;

  • flags: identifier , Press bit Who said Block Additional information for , Be similar to isa Bit fields in ;

  • reserved: Reserved location ;

  • invoke: A function pointer , Point to Block The calling address of the implementation ;

  • descriptor: Additional information , for example : Number of reserved variables stored 、Block Size 、 Conduct copy or dispose 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 in BLOCK_BYREF_NEEDS_FREE Do bit and operation , Simultaneous interpreting flags, Tell me that Block 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 , decision block_description_2;

  • BLOCK_HAS_CTOR: Owned or not Block Of C++ Destructor ;

  • BLOCK_IS_GC: Mark whether there is garbage collection ,OSX;

  • BLOCK_IS_GLOBAL: Whether the flag is global Block;

  • BLOCK_USE_STRET: And BLOCK_HAS_SIGNATURE relative , Judge if it's current Block Have a signature , be used for runtime Call... Dynamically when ;

  • BLOCK_HAS_SIGNATURE: Is there a signature ;

  • BLOCK_HAS_EXTENDED_LAYOUT: Whether there is expansion , decision block_description_3.

Runtime Copy

Get into _Block_copy Assembly code of function , Read x0 Register value , And print it :

image-1.png

  • Get into _Block_copy function , At present Block Marked as StackBlock.

Run directly to the end of the function , Read the return value x0 register , And print it :

image.png

  • Program runtime , At present Block accord with MallocBlock Conditions , after _Block_copy function , Will Block Copy to heap .

Block call

_Block_copy The function is finished , go back to viewDidLoad Method , Keep going Block Call to :

image.png

  • ldr x8, [x0, #0x10]x0 For the current Block, Read x0 + 16 byte Value , Assign a value to x8;

    • Block_layout In the structure , First address offset 16 byte , It's like skipping isaflags and reserved, Read invoke Function address , Assign a value to x8;
  • blr x8: Jump to invoke Function address , amount to Block Call to .

descriptor

When _Block_copy The function is finished , Print current Block by MallocBlock

image.png

  • It also prints out signaturecopydispose Data such as .

descriptor type

signaturecopydispose 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 and Block size
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
uintptr_t reserved;
uintptr_t size;
};
 Copy code 
  • Block_descriptor_2: Storage copy and dispose 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: Storage signature Signature and layout
#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 through Block Of descriptor 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 , return NULL;

  • 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 , return NULL;

  • 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 
  • 0xc3000002Block Of flags 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, prove Block_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, prove Block_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 Namely descriptor 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 
  • 0x00000001021422e0copy A function pointer ;

  • 0x00000001021422f0dispose A function pointer ;

  • 0x00000001021433e3signature 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 type viod

  • 8: Method 8 byte

  • @? Parameters 0 type

  • 0 Parameters 0 Starting position , from 0 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 , therefore size by 0.

  • argument 0 Parameters 0;

  • type encoding (@) '@?': among @ by id type ,? Unknown type ;

  • flags {isObject, isBlock}: That is Object type , It's also Block type ;

  • memory {offset = 0, size = 8} Parameters 0 from 0 byte Start , Occupy 8 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 for Block A copy of the object itself , Copy from stack to heap ;

  • Parameters arg Namely Block_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 to Block 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_0Block_descriptor_2 Medium copy;

  • __main_block_dispose_0Block_descriptor_2 Medium dispose.

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 
  • 0x0000000104f1a2e0copy 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 and dispose, Get... From memory Block_descriptor_2 Structure pointer of ;

  • call _Block_get_copy_fn function , Pass in bd2.

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 the copy 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_BLOCKBlock Type as a variable ;

  • BLOCK_FIELD_IS_BYREF: Use __block Decorated variable ;

  • BLOCK_FIELD_IS_WEAKweak Weakly referenced variables ;

  • BLOCK_BYREF_CALLER: The returned calling object , Handle block_byref An extra token will be added to the internal object memory , coordination flags 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 , send dest The target pointer to points to object;

  • Block Type as a variable , call _Block_copy function , send dest Point to copy to heap object;

  • Use __block Decorated variable , call _Block_byref_copy function , send dest Point to the... Copied to the heap byref.

_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 new byref Allocating memory in the heap :

    • Pile up byref Of forwarding Point to your ;

    • Put the... On the original stack byref Of forwarding It also points to... On the heap byref;

    • from byref_keep launch Block 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, Corresponding cpp 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, Corresponding cpp 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 for Block 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 is BLOCK_FIELD_IS_BYREF, call _Block_byref_copy function

  • 【 The second floor 】_Block_byref_copy function , take Block_byref Copy to the heap

    • adopt byref_keep function , call _Block_object_assign function , Pass in objc Instance object
  • 【 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 , Yes byref Object to do release 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 , from byref_destroy The of the initiating object release.

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 in objc Instance object .

  • Get into _Block_object_dispose function , Hit normal object type release Logic .

Please bring the original link to reprint ,thank
Similar articles

2021-09-15