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

    • ifblockNever call, soselfCan it be destroyed normally?
    • When running to__strong typeof(weakSelf) strongSelf = weakSelfOn the next line of,selfReference countingA minimum ofHow much is?

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.

  1. As the attribute

    @property (nonatomic,copy) void(^block)(void);

  2. As a parameter

    - (void) getDataWithBlock:(id(^)(id parameter))block;

  3. As a return value (navigation)

    - (MASConstraint * (^)(id))equalTo

structure

- (void)testFunc {self.block111 = ^{printf("block test code "); }; self.block111(); }Copy the code

theOCThe 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

  1. self.block = &__TestCode__testFunc_block_impl_0(A,B)

    1. A: __TestCode__testFunc_block_func_0
    2. B: __TestCode__testFunc_block_desc_0_DATA

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

  1. It has an internal constructor of the same name__TestCode__testFunc_block_impl_0
  2. Two attributes
    1. __block_impl impl

    2. __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.

  1. 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
  2. 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.

  1. __bloktheaWhat structure is encapsulated?
  2. structureaWhere exactly is the value of?
  3. structureaHow 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__blockModification of thea, generated a use__attribute__Modification of the__Block_byref_a_0Type of structure.
  • There’s one inside the structureisaPointer, indicating__Block_byref_a_0Its essence is also aOCobject

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

    • _bisStack the b
    • bValue 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:

  1. blockIn thebAnd outside theb, only values are passed, so even if external modifications are madebThe value of theta is not going to be correctblockthebMake an impact.
  2. __block a;theaPackaged into a structure, andblockInternal properties__Block_byref_a_0 *aThis is the stack structureaThe address of the
  3. At this point,blockThere is nocopyOperation, soblockThere are stacks, structuresaThere 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
  1. Called when a block is copied to the heap

  2. 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 *)&copy->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:

  1. The copy desPose function is generated.

  2. Copy call timing:

    1. whenblockforcopyIt is called automatically when the operation is performed__TestCode__testFunc_block_desc_0The inside of the__TestCode__testFunc_block_copy_0The function,__TestCode__testFunc_block_copy_0The function is called internally_Block_object_assignFunction.
    2. _Block_object_assignThe interior is based on passingflagsType toaforcopy,retainoperation
  3. Despose call timing:

    1. whenblockIt is called automatically when removed from the heap__TestCode__testFunc_block_desc_0In the__TestCode__testFunc_block_dispose_0The function,__TestCode__testFunc_block_dispose_0The function is called internally_Block_object_disposeFunction.
    2. _Block_object_disposetoaDo a release operation, something like thatrelease.

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:

  1. with__blockmodified
  2. with__strongEmbellish (@strongify)
  3. with__weakModify (@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:

  1. __Block_byref_id_object_copyA value of__Block_byref_id_object_copy_131
  2. __Block_byref_id_object_disposeA 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

  1. flags: 570425344saidBLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR, namely, (1 < < 25 | 1 < < 29)
  2. 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_assignoperation
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. toblockSelfthe_Block_object_assignoperation

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. toweakSelfdisposeoperation
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. toblockSelfdisposeoperation

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
  1. __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

  2. __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__blockDecorated object

Objects decorated with __block are written in two ways

  1. __block TestCode *blockSelf = weakSelf
  2. __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

  1. __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.

  2. __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:

  1. In the current scope, yesselftheretainCountPlus 1, minus 1 when out of scope
    1. __block typeof(self)blockSelf = self;
    2. __block NSObject *blockSelf = self;
    3. __strong typeof (self)strongSelf = self;
    4. __strong NSObject *strongSelf = self;
  2. rightselftheretainCountDo not make operation
    1. __weak typeof(self)weakSelf = self;
    2. __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

  1. Explore the nature of blocks
  2. Basic Principles of iOS – Exploring the Nature of Blocks (PART 1)
  3. Basic Principles of iOS – Exploring the Nature of Blocks (PART 2)
  4. IOS Block Part6: implementation of Block copy
  5. Os-block underlying parsing
  6. This article dissects the underlying source code of block and block. private
  7. Block_private.h
  8. runtime.c

If there is something wrong, welcome to spray ~

E-mail: 15076299703 @163.com