This paper mainly analyzes the block structure changes when holding __block, __weak, __strong modified objects.
And the specific effect that blocks have on reference counts of holding variables.
Think about:
@implementation TestCode
- (void)testFunc { @weakify(self) self.block111 = ^{ @strongify(self); Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, dispatch_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{dispatch_time(DISPATCH_TIME_NOW, dispatch_t)(2.0 * NSEC_PER_SEC))if (self) {
printf("\n\nstrongPerson1 Retain Count = %ld",CFGetRetainCount((__bridge CFTypeRef)(self)));
}else{
printf("self Retain Count = 0 \n"); }}); }; } - (void)dealloc {printf("\n");
printf("✅ 【dealloc】 Retain Count = 0");
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
TestCode *t = [[TestCode alloc]init];
[t testFunc];
if (t.block111) {
t.block111();
}
}
@end
Copy the code
Note: The following block111 is a block held by self
-
If I add and delete elements from NSMutableArray *arrayM in block111, does arrayM need to be modified with __block?
-
In block111, __strong typeof(weakSelf) strongSelf = weakSelf modification is performed on weakSelf
- if
block
Never call, soself
Can it be destroyed normally? - When running to
__strong typeof(weakSelf) strongSelf = weakSelf
On the next line of,self
Reference countingA minimum of
How much is?
- if
One: Basic introduction
define
A Block is an anonymous function with automatic variables (local variables). It is an extension of C and is essentially an OC object.
-
As the attribute
@property (nonatomic,copy) void(^block)(void);
-
As a parameter
- (void) getDataWithBlock:(id(^)(id parameter))block;
-
As a return value (navigation)
- (MASConstraint * (^)(id))equalTo
structure
- (void)testFunc {self.block111 = ^{printf("block test code "); }; self.block111(); }Copy the code
theOC
The code toc++
code
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk - fobjc - arc - fobjc - runtime = macosx - 10.13 XXXXXX. M
static void _I_TestCode_testFunc(TestCode * self, SEL _cmd) {
/// self.block = &__TestCode__testFunc_block_impl_0(A,B)
((void (*)(id, SEL, void (^ _Nonnull)()))(void *)objc_msgSend)
((id)self, sel_registerName("setBlock111:"), ((void (*)())&__TestCode__testFunc_block_impl_0
(
(void *)__TestCode__testFunc_block_func_0,1 / / parameter
&__TestCode__testFunc_block_desc_0_DATA/ / 2 __testcode__testfunc_block_desc_0 parameters,))); ((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("block111"())); }Copy the code
The basic idea of the previous code is that
-
self.block = &__TestCode__testFunc_block_impl_0(A,B)
- A:
__TestCode__testFunc_block_func_0
- B:
__TestCode__testFunc_block_desc_0_DATA
- A:
1. __TestCode__testFunc_block_impl_0
struct __TestCode__testFunc_block_impl_0 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_0* Desc;
__TestCode__testFunc_block_impl_0(void *fp, struct __TestCode__testFunc_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
Naming rules: __ Class name __ method name _block_IMPL_ Hierarchy
As you can see from the above code, the block is compiled into a __TestCode__testFunc_block_impl_0 structure
- It has an internal constructor of the same name
__TestCode__testFunc_block_impl_0
- Two attributes
-
__block_impl impl
-
__TestCode__testFunc_block_desc_0* Desc
-
2. __block_impl
struct __block_impl {
void *isa;// This points to &_NSConcretestackBlock
int Flags;
int Reserved;
void *FuncPtr; // The storage nature of the method is a __TestCode__testFunc_block_func_0 C function
};
Copy the code
You can see that there is an ISA pointer inside the __block_impl structure. So you can prove that a block is essentially an OC object.
The isa pointer to the __block_impl structure holds the &_NSConcretestackblock address. Block is of type _NSConcreteStackBlock.
- Seeing ISA reminds us of objc_class, so our block is essentially an object too. We know that instance objects -> class objects -> metaclasses form one of the ISA chains, and the __block_impl structure occupies the place of the middle class object
- The ISA pointer here will point to the metaclass, which is mainly used to describe the storage area of the block
__TestCode__testFunc_block_func_0
static void __TestCode__testFunc_block_func_0(struct __TestCode__testFunc_block_impl_0 *__cself) {
printf("Block test code");
}
Copy the code
__TestCode__testFunc_block_func_0 holds the code in the block
3. __TestCode__testFunc_block_desc_0
static struct __TestCode__testFunc_block_desc_0 {
size_t reserved;
size_t Block_size;
} __TestCode__testFunc_block_desc_0_DATA = { 0.sizeof(struct __TestCode__testFunc_block_impl_0)};
Copy the code
Basically, it stores the size of the block
Constructor of the same name__TestCode__testFunc_block_impl_0(void *fp, struct __TestCode__testFunc_block_desc_0 *desc, int flags=0)
__TestCode__testFunc_block_impl_0
(void *fp, struct __TestCode__testFunc_block_desc_0 *desc, int flags=0)
{
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
Copy the code
The function of the same name mainly assigns values to two attributes
void *fp
is(void *)__TestCode__testFunc_block_func_0__
__ struct __TestCode__testFunc_block_desc_0 *desc
is&__TestCode__testFunc_block_desc_0_DATA
5. Chart
Two: block structure when holding variables
The above analysis is that the block does not hold any external variables, but when the block holds external variables, it generates something extra.
Holds basic data types
The basic data types held are divided into whether or not __block is used, as follows: A is __block and B is not.
- (void)testFunc { __block NSInteger a = 0; NSInteger b = 0; self.block111 = ^{ a = 12 + b; Printf (" Block test code "); }; self.block111(); }Copy the code
thinking
A is changed from a value to an address by __block.
__blok
thea
What structure is encapsulated?- structure
a
Where exactly is the value of? - structure
a
How is memory managed?
To answer these questions, we need to look- (void)testFunc{}
Compiled source code:
The compiled:
static void _I_TestCode_testFunc(TestCode * self, SEL _cmd) {
__block NSInteger a = 0;
__attribute__((__blocks__(byref))) __Block_byref_a_0 a =
{
(void*)0.// isa
(__Block_byref_a_0 *)&a,// transfer of a address
0.// flags
sizeof(__Block_byref_a_0),// size
0/ / a value
};
NSInteger b = 0;
((void (*)(id, SEL, void (^ _Nonnull)()))(void *)objc_msgSend)
((id)self, sel_registerName("setBlock111:"),
// initialize the __TestCode__testFunc_block_impl_0 structure
((void (*)())&__TestCode__testFunc_block_impl_0
(
(void *)__TestCode__testFunc_block_func_0,
&__TestCode__testFunc_block_desc_0_DATA,
b,// The value passes b
(__Block_byref_a_0 *)&a,// Pass in the address of structure (object) A
570425344))); ((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("block111"())); }Copy the code
1. __Block_byref_a_0
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
NSInteger a;
};
// Struct a is generated
__attribute__((__blocks__(byref))) __Block_byref_a_0 a =
{
(void*)0.// isa
(__Block_byref_a_0 *)&a,// transfer of a address
0.// flags
sizeof(__Block_byref_a_0),// size
0/ / a value
};
Copy the code
- With the
__block
Modification of thea
, generated a use__attribute__Modification of the__Block_byref_a_0
Type of structure. - There’s one inside the structure
isa
Pointer, indicating__Block_byref_a_0
Its essence is also aOC
object
2. The __TestCode__testFunc_block_impl_0 structure
struct __TestCode__testFunc_block_impl_0 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_0* Desc;
NSInteger b;
__Block_byref_a_0 *a; // by ref
// constructor with the same name
__TestCode__testFunc_block_impl_0
(
void *fp,
struct __TestCode__testFunc_block_desc_0 *desc,
NSInteger _b,
__Block_byref_a_0 *_a,
int flags=0) : b(_b), a(_a->__forwarding) { ... }};Copy the code
New properties are generated:
-
NSInteger b
_b
isStack the b
b
Value passed
-
__Block_byref_a_0 *a
-
Since the assignment to __TestCode__testFunc_block_impl_0 passes the address of __block_byref_a_0a in the stack area, _a == &a.
-
Since _a->__forwarding is &_a, a in __TestCode__testFunc_block_impl_0 refers to a in the stack
-
Conclusion:
block
In theb
And outside theb
, only values are passed, so even if external modifications are madeb
The value of theta is not going to be correctblock
theb
Make an impact.__block a;
thea
Packaged into a structure, andblock
Internal properties__Block_byref_a_0 *a
This is the stack structurea
The address of the- At this point,
block
There is nocopy
Operation, soblock
There are stacks, structuresa
There is also a stack area
3. __TestCode__testFunc_block_desc_0
static struct __TestCode__testFunc_block_desc_0 {
size_t reserved;
size_t Block_size;
/ / copy function
void (*copy)(
struct __TestCode__testFunc_block_impl_0*,
struct __TestCode__testFunc_block_impl_0*
);
/ / the dispose function
void (*dispose)(struct __TestCode__testFunc_block_impl_0*);
} __TestCode__testFunc_block_desc_0_DATA =
{ 0.// reserved
sizeof(struct __TestCode__testFunc_block_impl_0), //size
__TestCode__testFunc_block_copy_0,//copy
__TestCode__testFunc_block_dispose_0//dispose
};
Copy the code
The copy dispose function is generated
a. __TestCode__testFunc_block_copy_0
-
Called when a block is copied to the heap
-
The implementation function is _Block_object_assign, which determines whether the object needs to be copied or simply assigned based on its flags.
// copy
static void __TestCode__testFunc_block_copy_0(struct __TestCode__testFunc_block_impl_0*dst, struct __TestCode__testFunc_block_impl_0*src){
_Block_object_assign(
(void*)&dst->a,
(void*)src->a,
8/*BLOCK_FIELD_IS_BYREF*/
);
}
Copy the code
_Block_object_assign
Function implementation inruntime.c
里
/** _Block_object_assign parameter flag Related // It is an object BLOCK_FIELD_IS_OBJECT = 3, // It is a block BLOCK_FIELD_IS_BLOCK = 7, // the __block variable BLOCK_FIELD_IS_BYREF = 8, Only secondary copy functions can use BLOCK_FIELD_IS_WEAK = 16, // block secondary function calls (telling internal implementations not to retain or copy) BLOCK_BYREF_CALLER = 128 **/
void _Block_object_assign(void *destAddr, const void *object, const int flags) {
// BLOCK_BYREF_CALLER block auxiliary function call (telling internal implementation not to retain or copy)
if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
//BLOCK_FIELD_IS_WEAK The __weak variable that can only be used by the auxiliary copy function
if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
_Block_assign_weak(object, destAddr);
}
else {
_Block_assign((void*)object, destAddr); }}// A variable modified by __block
else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF) {
/// Finally go this way
_Block_byref_assign_copy(destAddr, object, flags);
}
// is a block
else if((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) { _Block_assign(_Block_copy_internal(object, flags), destAddr); }// is an object
else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
_Block_retain_object(object);
_Block_assign((void*)object, destAddr); }}Copy the code
/*
Block_private.h
https://opensource.apple.com/source/libclosure/libclosure-73/Block_private.h
*/
enum {
BLOCK_DEALLOCATING = (0x0001), // runtime
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
BLOCK_NEEDS_FREE = (1 << 24), // runtime
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
BLOCK_IS_GC = (1 << 27), // runtime
BLOCK_IS_GLOBAL = (1 << 28), // compiler
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if ! BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30), // compiler
BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
};
struct Block_byref {
void *isa;
struct Block_byref *forwarding;
int flags; /* refcount; * /
int size;
};
/** runtime.c
http://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/BlocksRuntime/runtime.c
*/
static void *_Block_copy_class = _NSConcreteMallocBlock;
static void *_Block_copy_finalizing_class = _NSConcreteMallocBlock;
static int _Block_copy_flag = BLOCK_NEEDS_FREE;
static int _Byref_flag_initial_value = BLOCK_NEEDS_FREE | 2;
static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
struct Block_byref **destp = (struct Block_byref* *)dest;
struct Block_byref *src = (struct Block_byref *)arg;
// No operations are required
if (src->forwarding->flags & BLOCK_IS_GC) {
}
// Copy is required to the heap and reference counts are required
else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack
bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
// if its weak ask for an object (only matters under GC)
struct Block_byref *copy = (struct Block_byref*) _Block_allocator(src->size.false.isWeak);
copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
src->forwarding = copy; // patch stack to point to heap copy
copy->size = src->size;
if (isWeak) {
copy->isa = &_NSConcreteWeakBlockVariable; // mark isa field so it gets weak scanning
}
if (src->flags & BLOCK_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
copy->byref_keep = src->byref_keep;
copy->byref_destroy = src->byref_destroy;
(*src->byref_keep)(copy, src);
}
else {
// just bits. Blast 'em using _Block_memmove in case they're __strong
_Block_memmove(
(void *)©->byref_keep,
(void *)&src->byref_keep,
src->size - sizeof(struct Block_byref_header)); }}// It is already copied to the heap and only operates on reference counting
else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
// assign byref data block pointer into new Block
Destp = SRC ->forwarding = Block_byref *copy
_Block_assign(src->forwarding, (void **)destp);
}
static void (*_Block_assign)(void *value, void **destptr) = _Block_assign_default;
static void _Block_assign_default(void *value, void **destptr) {
*destptr = value;
}
Copy the code
Omitted code
struct Block_byref {
void *isa;
struct Block_byref *forwarding;
int flags; /* refcount; * /
int size;
};
static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
...
struct Block_byref *copy = (struct Block_byref*) _Block_allocator(src->size.false.isWeak);
copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
// Copy forwarding in the heap points to itself
copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
Forwarding in the stack refers to the new object in the heap
src->forwarding = copy; // patch stack to point to heap copycopy->size = src->size; .Destp = SRC ->forwarding = Block_byref *copy
_Block_assign(src->forwarding, (void **)destp);
}
Copy the code
As you can see, the first four members of Block_byref and __Block_byref_a_0 have the same type and can be converted to each other.
b. __TestCode__testFunc_block_dispose_0
// dispose
static void __TestCode__testFunc_block_dispose_0(struct __TestCode__testFunc_block_impl_0*src){
_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);
}
Copy the code
_Block_object_dispose
void _Block_object_dispose(const void *object, const int flags) {
//printf("_Block_object_dispose(%p, %x)\n", object, flags);
if (flags & BLOCK_FIELD_IS_BYREF) {
// Release the __block modified variable
_Block_byref_release(object);
}
else if ((flags & (BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_BLOCK) {
// Release the block referenced by the block
_Block_destroy(object);
}
else if ((flags & (BLOCK_FIELD_IS_WEAK|BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_OBJECT) {
// Release the object referenced by the block_Block_release_object(object); }}Copy the code
static void _Block_byref_release(const void *arg) {
struct Block_byref *shared_struct = (struct Block_byref *)arg;
int refcount;
shared_struct = shared_struct->forwarding;
if ((shared_struct->flags & BLOCK_NEEDS_FREE) == 0) {
return; // stack or GC or global
}
refcount = shared_struct->flags & BLOCK_REFCOUNT_MASK;
if (refcount <= 0) {
printf("_Block_byref_release: Block byref data structure at %p underflowed\n", arg);
}
else if ((latching_decr_int(&shared_struct->flags) & BLOCK_REFCOUNT_MASK) == 0) {
if(shared_struct->flags & BLOCK_HAS_COPY_DISPOSE) { (*shared_struct->byref_destroy)(shared_struct); } _Block_deallocator((struct Block_layout *)shared_struct); }}static void (*_Block_deallocator)(const void *) = (void(*) (const void *))free;
static int latching_decr_int(int *where) {
while (1) {
int old_value = *(volatile int *)where;
if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
return BLOCK_REFCOUNT_MASK;
}
if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
return 0;
}
if (OSAtomicCompareAndSwapInt(old_value, old_value- 1, (volatile int *)where)) {
return old_value- 1; }}}Copy the code
A variable decorated with __block is released using latching_decr_int to subtract the reference count until the count reaches zero.
Normal objects, blocks, are destroyed.
Summary:
-
The copy desPose function is generated.
-
Copy call timing:
- when
block
forcopy
It is called automatically when the operation is performed__TestCode__testFunc_block_desc_0
The inside of the__TestCode__testFunc_block_copy_0
The function,__TestCode__testFunc_block_copy_0
The function is called internally_Block_object_assign
Function. _Block_object_assign
The interior is based on passingflags
Type toa
forcopy
,retain
operation
- when
-
Despose call timing:
- when
block
It is called automatically when removed from the heap__TestCode__testFunc_block_desc_0
In the__TestCode__testFunc_block_dispose_0
The function,__TestCode__testFunc_block_dispose_0
The function is called internally_Block_object_dispose
Function. _Block_object_dispose
toa
Do a release operation, something like thatrelease
.
- when
4. __TestCode__testFunc_block_func_0
__TestCode__testFunc_block_func_0 is block code stored in the __block_impl structure
static void __TestCode__testFunc_block_func_0(struct __TestCode__testFunc_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
NSInteger b = __cself->b; // bound by copy
(a->__forwarding->a) = 12 + b;
printf("Block test code");
}
Copy the code
__cself is the block we define
A ->__forwarding actually changes our heap (Block_byref) copy.
The following figure shows a custom structure in testcode.m:
struct __Block_byref_a_0 { void *__isa; struct __Block_byref_a_0 *__forwarding; int __flags; int __size; NSInteger a; }; struct __block_impl { void *isa; &_nsConcretestAckBlock int Flags; int Reserved; void *FuncPtr; // The storage nature of the method is a __TestCode__testFunc_block_func_0 c function}; struct __TestCode__testFunc_block_impl_0 { struct __block_impl impl; struct __TestCode__testFunc_block_desc_0* Desc; struct __Block_byref_a_0 *a; // by ref };Copy the code
5. Chart
__block NSInteger a = 0; NSInteger b = 0; self.block111 = ^{ a = 12 + b; Printf (" Block test code "); };Copy the code
Holding object type
References to object types fall into three categories:
- with
__block
modified - with
__strong
Embellish (@strongify) - with
__weak
Modify (@weakify)
⚠️ Note that the following code generates circular references, which will be examined in detail later
- (void)testFunc { __weak typeof(self)weakSelf = self; __block TestCode *blockSelf = weakSelf; self.block111 = ^{ __strong typeof(weakSelf)strongSelf = weakSelf; void(^block222)(void) = ^{ blockSelf = strongSelf; Printf ("\nblock test code \n"); }; block222(); }; self.block111(); }Copy the code
Compile the above code:
static void _I_TestCode_testFunc(TestCode * self, SEL _cmd) {
__attribute__((objc_ownership(weak))) typeof(self)weakSelf = self;
__attribute__((__blocks__(byref))) __Block_byref_blockSelf_0 blockSelf =
{
(void*)0,
(__Block_byref_blockSelf_0 *)&blockSelf,
33554432.sizeof(__Block_byref_blockSelf_0),
__Block_byref_id_object_copy_131,
__Block_byref_id_object_dispose_131,
weakSelf
};
/ / create __TestCode__testFunc_block_impl_1
((void (*)(id, SEL, void (^ _Nonnull)()))(void *)objc_msgSend)((id)self, sel_registerName("setBlock111:"), ((void (*)())&__TestCode__testFunc_block_impl_1
(/ / parameters:
(void *)__TestCode__testFunc_block_func_1,
&__TestCode__testFunc_block_desc_1_DATA,
weakSelf,
(__Block_byref_blockSelf_0 *)&blockSelf,
570425344))); ((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("block111"())); }Copy the code
1. __Block_byref_blockSelf_0
struct __Block_byref_blockSelf_0 {
// [value: 0] [8 bytes]
void *__isa;
// [value = &blockself],[8 bytes]
__Block_byref_blockSelf_0 *__forwarding;
int __flags;// [value 33554432],[4 bytes]
int __size;// [sizeof(__Block_byref_blockSelf_0)],[4 bytes]
// [__Block_byref_id_object_copy_131] [8 bytes]
void (*__Block_byref_id_object_copy)(void*, void*);
// [__Block_byref_id_object_dispose_131],[8 bytes]
void (*__Block_byref_id_object_dispose)(void*);
TestCode *__strong blockSelf;//【weakSelf】[8 bytes]
};
/// Total 48 bytes
Copy the code
After self is decorated with a __block, blockPerson is wrapped into a structure similar to __Block_byref_a_0
There are only two more functions than __Block_byref_a_0:
__Block_byref_id_object_copy
A value of__Block_byref_id_object_copy_131
__Block_byref_id_object_dispose
A value of__Block_byref_id_object_dispose_131
⚠️ It is worth noting that blockSelf is decorated with __strong, thus creating circular references! __block Typeof (weakSelf)blockSelf = weakSelf; More on that below
__Block_byref_id_object_copy_131 and __Block_byref_id_object_dispose_131
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, * (void((*)char)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void((* *)char*)src + 40), 131);
}
Copy the code
The internal call function is _Block_object_assign
DST and SRC are Pointers to blockSelf, the __Block_byref_blockSelf_0 structure
__Block_byref_blockSelf_0 contains 48 bytes, so (char*) DST + 40 and (char) SRC + 40 find TestCode *__strong blockSelf
Finally flags transmission is 131 = 3 | 128: BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_CALLER
Call timing: Block performs copy, which will be discussed later.
2. __TestCode__testFunc_block_impl_1
struct __TestCode__testFunc_block_impl_1 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_1* Desc;
TestCode *const __weak weakSelf;
__Block_byref_blockSelf_0 *blockSelf; // by ref
__TestCode__testFunc_block_impl_1(
void *fp,
struct __TestCode__testFunc_block_desc_1 *desc,
TestCode *const __weak _weakSelf,
__Block_byref_blockSelf_0 *_blockSelf,
int flags=0) : weakSelf(_weakSelf), blockSelf(_blockSelf->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
Two member variables are generated: Person *__weak weakPerson; , __Block_byref_blockPerson_0 * blockPerson;
**a. __block_impl impl **
There is no change in the structure
flags
:570425344
saidBLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR
, namely, (1 < < 25 | 1 < < 29)FuncPtr
:__TestCode__testFunc_block_func_1
3. __TestCode__testFunc_block_desc_1
There is no change in the structure
Desc: structure __TestCode__testFunc_block_desc_1_DATA
static struct __TestCode__testFunc_block_desc_1 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __TestCode__testFunc_block_impl_1*, struct __TestCode__testFunc_block_impl_1*);
void (*dispose)(struct __TestCode__testFunc_block_impl_1*);
} __TestCode__testFunc_block_desc_1_DATA = {
0.sizeof(struct __TestCode__testFunc_block_impl_1),
__TestCode__testFunc_block_copy_1,
__TestCode__testFunc_block_dispose_1
};
Copy the code
It is worth noting that the implementation function of copy and Dispose
/ / copy function
static void __TestCode__testFunc_block_copy_1(struct __TestCode__testFunc_block_impl_1*dst, struct __TestCode__testFunc_block_impl_1*src)
{
_Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_assign((void*)&dst->blockSelf, (void*)src->blockSelf, 8/*BLOCK_FIELD_IS_BYREF*/);
}
/ / the dispose function
static void __TestCode__testFunc_block_dispose_1(struct __TestCode__testFunc_block_impl_1*src)
{
_Block_object_dispose((void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_dispose((void*)src->blockSelf, 8/*BLOCK_FIELD_IS_BYREF*/);
}
Copy the code
A. toweakSelf
的 _Block_object_assign
operation
void _Block_object_assign(void *destAddr, const void *object, const int flags) {
// BLOCK_BYREF_CALLER block auxiliary function call (telling internal implementation not to retain or copy)
if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) { ... }
// A variable modified by __block
else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF) { ... }
// is a block
else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) { ... }
// is an object
else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
_Block_retain_object(object);
_Block_assign((void*)object, destAddr); }}Copy the code
That is, a _Block_retain_object operation is performed directly on the object
However, the _Block_retain_object function in ARC does not add +1 to the reference count of the object.
static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
static void _Block_retain_object_default(const void *ptr) {
if(! ptr)return;
}
Copy the code
B. toblockSelf
the_Block_object_assign
operation
The _Block_byref_assign_copy function is eventually called
static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
struct Block_byref **destp = (struct Block_byref* *)dest;
struct Block_byref *src = (struct Block_byref *)arg;
// No operations are required
if (src->forwarding->flags & BLOCK_IS_GC) {
}
// Copy is required to the heap and reference counts are required
else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
struct Block_byref *copy = (struct Block_byref*) _Block_allocator(src->size.false.isWeak);
copy->flags = src->flags | _Byref_flag_initial_value;
copy->forwarding = copy;
src->forwarding = copy;
copy->size = src->size;
if (isWeak) {
copy->isa = &_NSConcreteWeakBlockVariable;
}
if (src->flags & BLOCK_HAS_COPY_DISPOSE) {
// call __Block_byref_id_object_copy in __Block_byref_blockSelf_0
/// Execute byref's byref_keep function (assign, with the BLOCK_BYREF_CALLER flag) to manage the memory of captured objects
copy->byref_keep = src->byref_keep;
copy->byref_destroy = src->byref_destroy;
(*src->byref_keep)(copy, src);
}
else{... }}// It is already copied to the heap and only operates on reference counting
else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) { ... }
_Block_assign(src->forwarding, (void **)destp);
}
Copy the code
Note that this structure is found at block_private.h:
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 the code
Byref_keep is the __Block_byref_id_object_copy function in blockSelf, that is, __Block_byref_id_object_copy_131
So the call is
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, * (void((*)char)src + 40), 131);
}
/ / translated into
static void __Block_byref_id_object_copy_131(__Block_byref_blockSelf_0 *dst, __Block_byref_blockSelf_0 *src) {
_Block_object_assign(
dst->blockSelf,
*(void * )(src->blockSelf),
BLOCK_FIELD_IS_OBJECT|BLOCK_FIELD_IS_CALLER
);
}
Copy the code
Continue to see _Block_object_assign
void _Block_object_assign(void *destAddr, const void *object, const int flags) {
// BLOCK_BYREF_CALLER block auxiliary function call (telling internal implementation not to retain or copy)
if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
//BLOCK_FIELD_IS_WEAK The __weak variable that can only be used by the auxiliary copy function
if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
_Block_assign_weak(object, destAddr);
}
else {
_Block_assign((void*)object, destAddr); }}else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF) { ... }
else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) { ... }
else if((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) { ... }}static void (*_Block_assign)(void *value, void **destptr) = _Block_assign_default;
static void _Block_assign_default(void *value, void **destptr) {
*destptr = value;
}
Copy the code
The _Block_assign function is run, assigning __Block_byref_blockSelf_0 blockSelf of the stack to Block_byref copy
C. toweakSelf
的 dispose
operation
static void __TestCode__testFunc_block_dispose_1(struct __TestCode__testFunc_block_impl_1*src)
{
_Block_object_dispose(
(void*)src->self,
3/*BLOCK_FIELD_IS_OBJECT*/
);
_Block_object_dispose(
(void*)src->blockSelf,
8/*BLOCK_FIELD_IS_BYREF*/
);
}
void _Block_object_dispose(const void *object, const int flags) {
if (flags & BLOCK_FIELD_IS_BYREF) { ... }
else if((flags & (BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_BLOCK) {... }else if ((flags & (BLOCK_FIELD_IS_WEAK|BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_OBJECT) {
// Release the object referenced by the block_Block_release_object(object); }}static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
static void _Block_release_object_default(const void *ptr) {
if(! ptr)return;
}
Copy the code
As you can see, _Block_release_object is eventually called, and there is no internal reference counting.
D. toblockSelf
的 dispose
operation
This eventually leads to the _Block_byref_release function:
static void _Block_byref_release(const void *arg) {
struct Block_byref *shared_struct = (struct Block_byref *)arg;
int refcount;
shared_struct = shared_struct->forwarding;
if ((shared_struct->flags & BLOCK_NEEDS_FREE) == 0) {
return;
}
refcount = shared_struct->flags & BLOCK_REFCOUNT_MASK;
if (refcount <= 0) {}else if ((latching_decr_int(&shared_struct->flags) & BLOCK_REFCOUNT_MASK) == 0) {
/// the main call
if(shared_struct->flags & BLOCK_HAS_COPY_DISPOSE) { (*shared_struct->byref_destroy)(shared_struct); } _Block_deallocator((struct Block_layout *)shared_struct); }}Copy the code
*shared_struct->byref_destroy (shared_struct)
This is the __Block_byref_id_object_dispose_131 function in __Block_byref_blockSelf_0 *blockSelf
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void((* *)char*)src + 40), 131);
}
/ / / translated into:
static void __Block_byref_id_object_dispose_131(__Block_byref_blockSelf_0 *src) {
_Block_object_dispose
(
*(void * *) (src->blockSelf),
BLOCK_FIELD_IS_OBJECT|BLOCK_FIELD_IS_CALLER
);
}
Copy the code
e. __TestCode__testFunc_block_func_1
Look at __TestCode__testFunc_block_func_1, how is the second layer block222 created in the function
{__strong typeof(weakSelf)strongSelf = weakSelf;
void(^block222)(void) = ^{
blockSelf = strongSelf;
printf("\nblock test code \n");
};
block222();
}
static void __TestCode__testFunc_block_func_1(struct __TestCode__testFunc_block_impl_1 *__cself) {
__Block_byref_blockSelf_0 *blockSelf = __cself->blockSelf; // bound by ref
TestCode *const __weak weakSelf = __cself->weakSelf; // bound by copy
///__strong typeof(weakSelf)strongSelf = weakSelf;
__attribute__((objc_ownership(strong))) typeof(weakSelf)strongSelf = weakSelf;
Create block222, which is: __TestCode__testFunc_block_impl_0
void(*block222)(void) = ((void (*)())&__TestCode__testFunc_block_impl_0((void *)__TestCode__testFunc_block_func_0,&__TestCode__testFunc_block_desc_0_DATA,strongSelf,(__Block_byref_blockSelf_0 *)blockSelf,570425344));
// call: block222()
((void (*)(__block_impl *))((__block_impl *)block222)->FuncPtr)((__block_impl *)block222);
}
Copy the code
Block222 is a __TestCode__testFunc_block_impl_0 structure
4. __TestCode__testFunc_block_impl_0
struct __TestCode__testFunc_block_impl_0 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_0* Desc;
TestCode *const __strong strongSelf;
__Block_byref_blockSelf_0 *blockSelf; // by ref
__TestCode__testFunc_block_impl_0(
void *fp,
struct __TestCode__testFunc_block_desc_0 *desc,
TestCode *const __strong _strongSelf,
__Block_byref_blockSelf_0 *_blockSelf,
int flags=0) : strongSelf(_strongSelf), blockSelf(_blockSelf->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
The structure is similar to the __TestCode__testFunc_block_impl_1 structure.
Only blockSelf is the blockSelf in the upper block (i.e., block111)
It’s worth noting here that TestCode *const __strong strongSelf;
The remaining structures are pretty much the same as those analyzed earlier:
static void __TestCode__testFunc_block_func_0(struct __TestCode__testFunc_block_impl_0 *__cself) {
__Block_byref_blockSelf_0 *blockSelf = __cself->blockSelf; // bound by ref
TestCode *const __strong strongSelf = __cself->strongSelf; // bound by copy
(blockSelf->__forwarding->blockSelf) = strongSelf;
printf("\nblock test code \n");
}
static void __TestCode__testFunc_block_copy_0(struct __TestCode__testFunc_block_impl_0*dst, struct __TestCode__testFunc_block_impl_0*src) {_Block_object_assign((void*)&dst->blockSelf, (void*)src->blockSelf, 8/*BLOCK_FIELD_IS_BYREF*/); _Block_object_assign((void*)&dst->strongSelf, (void*)src->strongSelf, 3/*BLOCK_FIELD_IS_OBJECT*/); }static void __TestCode__testFunc_block_dispose_0(struct __TestCode__testFunc_block_impl_0*src) {_Block_object_dispose((void*)src->blockSelf, 8/*BLOCK_FIELD_IS_BYREF*/); _Block_object_dispose((void*)src->strongSelf, 3/*BLOCK_FIELD_IS_OBJECT*/); }static struct __TestCode__testFunc_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __TestCode__testFunc_block_impl_0*, struct __TestCode__testFunc_block_impl_0*);
void (*dispose)(struct __TestCode__testFunc_block_impl_0*);
} __TestCode__testFunc_block_desc_0_DATA = { 0.sizeof(struct __TestCode__testFunc_block_impl_0), __TestCode__testFunc_block_copy_0, __TestCode__testFunc_block_dispose_0};
Copy the code
Three: hold variable reference count operation:
Since copy and Dispose do not modify the reference count in ARC, when will reference count be handled?
Through the analysis of holding variables, the following characteristics can be summarized
1. Use __strong and __weak
- (void)testFunc { printf("\n Retain Count = %ld",CFGetRetainCount((__bridge CFTypeRef)(self))); __weak typeof (self)weakSelf1 = self; Printf ("\n [__weak Typeof (self)weakSelf] RetainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)(self))); __weak TestCode *weakSelf2 = self; Printf ("\n [__weak TestCode *weakSelf2] RetainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)(self))); __strong typeof(self)strongSelf1 = self; Printf ("\n [__strong typeof(self)strongSelf1] RetainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)(self))); __strong TestCode *strongSelf2 = self; Printf ("\n [__strong TestCode *strongSelf2] RetainCount = %ld",CFGetRetainCount((__bridge CFTypeRef)(self))); self.block111 = ^{ [weakSelf1 class]; [weakSelf2 class]; [strongSelf1 class]; [strongSelf2 class]; }; } /** log: Retain Count = 1 【__weak Typeof (self)weakSelf】 Retain Count = 1 【__weak TestCode *weakSelf2】 Retain Count = 1 【__strong Typeof (self)strongSelf1】 Retain Count = 2 【__strong TestCode *strongSelf2】 Retain Count = 3 */Copy the code
Compiled code:
static void _I_TestCode_testFunc(TestCode * self, SEL _cmd) {
printf("...");
__attribute__((objc_ownership(weak))) typeof (self)weakSelf1 = self;
__attribute__((objc_ownership(weak))) TestCode *weakSelf2 = self;
__attribute__((objc_ownership(strong))) typeof(self)strongSelf1 = self;
__attribute__((objc_ownership(strong))) TestCode *strongSelf2 = self;
((void (*)(id, SEL, void (^ _Nonnull)()))(void *)objc_msgSend)((id)self, sel_registerName("setBlock111:"), ((void (*)())&__TestCode__testFunc_block_impl_0((void *)__TestCode__testFunc_block_func_0, &__TestCode__testFunc_block_desc_0_DATA, strongSelf1, strongSelf2, 570425344)));
}
struct __TestCode__testFunc_block_impl_0 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_0* Desc;
TestCode *const __weak weakSelf1;
TestCode *__weak weakSelf2;
__strong typeof (self) strongSelf1;
TestCode *__strong strongSelf2;
// Constructor of the same name__TestCode__testFunc_block_impl_0(...) {... }};Copy the code
-
__weak
__weak TestCode *weakSelf1 = self and __weak Typeof (self)weakSelf2 = self
__attribute__((objc_ownership(weak)))
And __TestCode__testFunc_block_impl_0 is weak reference to weakSelf1 and weakSelf2
-
__strong
__strong typeof(self)strongSelf1 = self and __strong TestCode *strongSelf2 = self
__attribute__((objc_ownership(strong)))
And __TestCode__testFunc_block_impl_0 has strong references to both strongSelf1 and strongSelf2
2. Use__block
Decorated object
Objects decorated with __block are written in two ways
__block TestCode *blockSelf = weakSelf
__block typeof(weakSelf)blockSelf2 = weakSelf
- (void)testFunc {
printf("\n Retain Count = %ld",CFGetRetainCount((__bridge CFTypeRef)(self)));
__weak typeof(self)weakSelf = self;
__block TestCode *blockSelf1 = weakSelf;
printf("\n 【__block TestCode *blockSelf1 = weakSelf】 Retain Count = %ld",CFGetRetainCount((__bridge CFTypeRef)(self)));
__block typeof(weakSelf)blockSelf2 = weakSelf;
printf("\n 【__block typeof(weakSelf)blockSelf2 = weakSelf】 Retain Count = %ld",CFGetRetainCount((__bridge CFTypeRef)(self)));
self.block111 = ^{
[blockSelf1 class];
[blockSelf2 class];
};
}
/**log: Retain Count = 1 【__block TestCode *blockSelf1 = weakSelf】 Retain Count = 2 【__block typeof(weakSelf)blockSelf2 = WeakSelf 】 Retain Count = 2 */
Copy the code
Compile the above code:
struct __TestCode__testFunc_block_impl_0 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_0* Desc;
__Block_byref_blockSelf1_0 *blockSelf1; // by ref
__Block_byref_blockSelf2_1 *blockSelf2; // by ref
// Constructor of the same name__TestCode__testFunc_block_impl_0(...) {... }};struct __Block_byref_blockSelf1_0 {
void *__isa;
__Block_byref_blockSelf1_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
TestCode *__strong blockSelf1;
};
struct __Block_byref_blockSelf2_1 {
void *__isa;
__Block_byref_blockSelf2_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
typeof (weakSelf) blockSelf2;
};
Copy the code
Two member variables are generated in __TestCode__testFunc_block_impl_0
-
__block TestCode *blockSelf = weakSelf
TestCode *__strong blockSelf1; A strong reference is made to self, resulting in a +1 self reference count, resulting in a circular reference.
-
__block typeof(weakSelf)blockSelf2 = weakSelf;
Typeof (weakSelf) blockSelf2 Refers to self as a weak reference, and the reference count does not have +1 operation.
3. Added:
- In the current scope, yes
self
theretainCount
Plus 1, minus 1 when out of scope__block typeof(self)blockSelf = self;
__block NSObject *blockSelf = self;
__strong typeof (self)strongSelf = self;
__strong NSObject *strongSelf = self;
- right
self
theretainCount
Do not make operation__weak typeof(self)weakSelf = self;
__weak NSObject *weakSelf = self;
Note:
Reference counts to objects with these modifier statements are valid only in the current scope. The key to having a block make a circular reference is this
Whether an object is strongly referenced in the __TestCode__testFunc_block_impl_0 block structure.
Think about the answer
@implementation TestCode - (void)testFunc { @weakify(self) self.block111 = ^{ @strongify(self); Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ if (self) { printf("\n\nstrongPerson1 Retain Count = %ld",CFGetRetainCount((__bridge CFTypeRef)(self))); }else{ printf("self Retain Count = 0 \n"); }}); }; } - (void)dealloc { printf("\n"); Printf ("✅ [dealloc] Retain Count = 0"); } @endCopy the code
Compiled code:
/// block111 structure
struct __TestCode__testFunc_block_impl_1 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_1* Desc;
TestCode *const __weak self_weak_;/ / if the reference
__TestCode__testFunc_block_impl_1(void *fp, struct __TestCode__testFunc_block_desc_1 *desc, TestCode *const __weak _self_weak_, int flags=0) : self_weak_(_self_weak_) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};/// the code stored in block111
static void __TestCode__testFunc_block_func_1(struct __TestCode__testFunc_block_impl_1 *__cself) {
TestCode *const __weak self_weak_ = __cself->self_weak_; // bound by copy
try {} catch(...). {}#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
__attribute__((objc_ownership(strong))) __typeof__(self) self = self_weak_;
#pragma clang diagnostic pop
;
dispatch_after(dispatch_time((0ull), (int64_t) (2.0 * 1000000000ull)), dispatch_get_main_queue(), ((void (*)())&__TestCode__testFunc_block_impl_0((void *)__TestCode__testFunc_block_func_0, &__TestCode__testFunc_block_desc_0_DATA, self, 570425344)));
}
// block222 structure
struct __TestCode__testFunc_block_impl_0 {
struct __block_impl impl;
struct __TestCode__testFunc_block_desc_0* Desc;
__strong typeof (self) self;/ / / strong reference
__TestCode__testFunc_block_impl_0(void *fp, struct __TestCode__testFunc_block_desc_0 *desc, __strong typeof (self) _self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
** The following block111 is a block held by self
-
If I add and delete elements from NSMutableArray *arrayM in block111, does arrayM need to be modified with __block?
A: No, because the address to which the arrayM pointer points is not modified
-
In block111, __strong typeof(weakSelf) strongSelf = weakSelf modification is performed on weakSelf
-
If the block is never called, can self be destroyed normally?
Answer: It can be destroyed
__TestCode__testFunc_block_impl_0 is created when block is called
Causes self to be strongly referenced internally by __TestCode__testFunc_block_impl_0
Thus self cannot be destroyed as long as __TestCode__testFunc_block_impl_0 is not destroyed
-
What is the minimum self reference count when running to the next line of __strong Typeof (weakSelf) strongSelf = weakSelf?
A: At least 2
But out of the scope of __strong Typeof (weakSelf) strongSelf = weakSelf, self’s reference count is automatically reduced by 1
-
Refer to the article
- Explore the nature of blocks
- Basic Principles of iOS – Exploring the Nature of Blocks (PART 1)
- Basic Principles of iOS – Exploring the Nature of Blocks (PART 2)
- IOS Block Part6: implementation of Block copy
- Os-block underlying parsing
- This article dissects the underlying source code of block and block. private
- Block_private.h
- runtime.c
If there is something wrong, welcome to spray ~
E-mail: 15076299703 @163.com