I briefly address iOS Block implementation principle of a comprehensive analysis

The underlying structure of a Block


void blockTest()
{
    void (^block)(void) = ^{
        NSLog(@"Hello World!"); }; block(); } int main(int argc, char * argv[]) { @autoreleasepool { blockTest(); }}Copy the code

To see how the compiler implements blocks, type clang-rewrite-objc main.m in the terminal and generate main. CPP C++ files in the current directory as follows:


struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_0048d2_mi_0);
    }
    

static struct __blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blockTest_block_desc_0_DATA = { 0.sizeof(struct __blockTest_block_impl_0)};

void blockTest(a)
{
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

int main(int argc, char * argv[]) {
    /* @autoreleasepool */{ __AtAutoreleasePool __autoreleasepool; blockTest(); }}static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0.2 };

Copy the code

Let’s look at them one by one

__blockTest_block_impl_0

struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

__blockTest_block_impl_0 is a C++ implementation of Block. It is a structure that represents the first (0) Block in blockTest. It usually contains two member variables, __block_impl impl, __blockTest_block_desc_0* Desc, and a constructor.

__block_impl

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

Copy the code

__block_impl is also a structure

  • * ISA: Isa pointer to a class object of three types: _NSConcreteStackBlock, _NSConcreteGlobalBlock, _NSConcreteMallocBlock, in this case of the _NSConcreteStackBlock type.
  • Flags: Block load information (reference count and type information), stored by bit.
  • Reserved: Reserved variables.
  • *FuncPtr: a pointer toBlockThe function called at execution time, i.eBlockBlocks of code that need to be executed. In this case, yes__blockTest_block_func_0Function.

__blockTest_block_desc_0

static struct __blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __blockTest_block_desc_0_DATA = { 0.sizeof(struct __blockTest_block_impl_0)};

Copy the code

__blockTest_block_desc_0 is a structure containing two member variables:

  • Reserved:BlockThe reserved space required for the version upgrade is 0.
  • Block_size:BlockSize (sizeof(struct __blockTest_block_impl_0)).

__blockTest_block_desc_0_DATA is an instance of __blockTest_block_desc_0.

__blockTest_block_func_0

__blockTest_block_func_0 is the function called when the Block is executed, and the argument is a pointer of type __blockTest_block_impl_0.

static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_0048d2_mi_0);
    }

Copy the code

blockTest

void blockTest(a)
{
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

Copy the code

The first part defines the Block

void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA));

Copy the code

We see that the block becomes a pointer to an instance of the __blockTest_block_impl_0 structure instantiated by the __blockTest_block_impl_0 constructor, __blockTest_block_impl_0 takes two arguments when initialized:

  • __blockTest_block_func_0:BlockBlock function pointer.
  • __blockTest_block_desc_0_DATA: initialized as a static global variable__main_block_desc_0Structure instance pointer to.

The second part calls the Block

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);

Copy the code

((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr) Find the __blockTest_block_func_0 function by using the block->FuncPtr pointer and convert it to (void (*)(__block_impl *)) type. ((__block_impl *)block) then passes the block as an argument to the function call.

Flags

In __block_impl we see Flags, now to go into more detail.

Here block_private. h can see Flags:

// Values for Block_layout->flags to describe block objects
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 };Copy the code

A brief introduction to the interpretation of block structure after block (1) -Clang rewriting:

That is, in general, a block’s flags member defaults to 0. If a block requires copy AIDS such as Block_copy() and Block_release, it is set to 1 << 25, which is BLOCK_HAS_COPY_DISPOSE. You can search for numerous blog posts about the Block_copy method, which involves BLOCK_HAS_COPY_DISPOSE.

To summarize the use of enumeration classes, the first 16 bits are both tokens and reference counts:

  • BLOCK_DEALLOCATING: Releases the mark. The common BLOCK_NEEDS_FREE bit and operation is passed to Flags to indicate that the block can be freed.
  • BLOCK_REFCOUNT_MASK: An optional parameter for general participation in judging the reference count.
  • BLOCK_NEEDS_FREE: Indicates that the block can be freed by setting the enumeration bit. Heap block = nsConcretemalLocblock = nsConcretemalLocblock
  • BLOCK_HAS_COPY_DISPOSE: Whether to have a copy helper function.
  • BLOCK_HAS_CTOR: Dispose function or not.
  • BLOCK_IS_GC: Whether to enable Garbage Collection.
  • BLOCK_HAS_SIGNATURE: Checks whether the current block has a signature, as opposed to BLOCK_USE_STRET. Used for dynamic invocation at Runtime.

Block intercept variable

Intercept the value of the auto variable

We see an error when modifying a variable directly in a block. Why?

void blockTest()
{
    int num = 10;
    void (^block)(void) = ^{
        NSLog(@"%d",num); }; num = 20; block(); } int main(int argc, char * argv[]) { @autoreleasepool { blockTest(); }}Copy the code

The printed result is 10, and the code rewritten by clang is as follows:

struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  int num;
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int _num, int flags=0) : num(_num) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
  int num = __cself->num; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_3c2714_mi_0,num);
    }
    
    void blockTest(a)
{
    int num = 10;
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, num));
    num = 20;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

Copy the code

Int num; __blockTest_block_impl_0; Void *fp, struct __blockTest_block_desc_0 *desc, int _num, int flags=0, You can see that the third argument is just the value of the variable, which explains why 10 is printed because the **block intercepts the value **.

Use static to decorate variables

void blockTest()
{
    static int num = 10;
    void (^block)(void) = ^{
        NSLog(@"%d",num);
        num = 30;
    };
    num = 20;
    block();
    NSLog(@"%d",num);
}

Copy the code

You can change the variables inside the block and print 20,30. The code rewritten by Clang looks like this:

struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  int *num;
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int *_num, int flags=0) : num(_num) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
  int *num = __cself->num; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_0,(*num));
        (*num) = 30;
    }
    
    void blockTest(a)
{
    static int num = 10;
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, &num));
    num = 20;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_1,num);
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

Copy the code

__blockTest_block_impl_0 has a member variable int *num; This time, the **block intercepts a pointer, so it can change the value of a variable internally through a pointer. It can also change the value of a variable externally. So why was the pointer passed earlier? Because variables are on the stack and scoped within the function blockTest, it is possible that the variable will be destroyed before the block, and then the block will have a problem accessing the variable through a pointer. Static modified variables are not destroyed, so don’t worry.

The global variable

int num = 10;

void blockTest()
{
    void (^block)(void) = ^{
        NSLog(@"%d",num);
        num = 30;
    };
    num = 20;
    block();
    NSLog(@"%d",num);
}

Copy the code

It prints 20,30. The code rewritten by Clang looks like this:

int num = 10;


struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_1875c6_mi_0,num);
        num = 30;
    }

Copy the code

Quite simply, the __blockTest_block_impl_0 initialization does not take num as a parameter, and __blockTest_block_func_0 also accesses global variables directly.

Conclusion:

Variable types Whether to capture inside the block access
Local auto variable is Value passed
Local static variable is Pointer passed
The global variable no Direct access to the

Use __block to decorate variables

void blockTest()
{
    __block int num = 10;
    void (^block)(void) = ^{
        NSLog(@"%d",num);
        num = 30;
    };
    num = 20;
    block();
    NSLog(@"%d",num);
}

Copy the code

Clang has the same effect as using static modifier variables.

struct __Block_byref_num_0 {
  void *__isa;
__Block_byref_num_0 *__forwarding;
 int __flags;
 int __size;
 int num;
};

struct __blockTest_block_impl_0 {
  struct __block_impl impl;
  struct __blockTest_block_desc_0* Desc;
  __Block_byref_num_0 *num; // by ref
  __blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, __Block_byref_num_0 *_num, int flags=0) : num(_num->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {
  __Block_byref_num_0 *num = __cself->num; // bound by ref

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_0,(num->__forwarding->num));
        (num->__forwarding->num) = 30;
    }
    
static void __blockTest_block_copy_0(struct __blockTest_block_impl_0*dst, struct __blockTest_block_impl_0*src) {_Block_object_assign((void*)&dst->num, (void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/); }static void __blockTest_block_dispose_0(struct __blockTest_block_impl_0*src) {_Block_object_dispose((void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/); }static struct __blockTest_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __blockTest_block_impl_0*, struct __blockTest_block_impl_0*);
  void (*dispose)(struct __blockTest_block_impl_0*);
} __blockTest_block_desc_0_DATA = { 0.sizeof(struct __blockTest_block_impl_0), __blockTest_block_copy_0, __blockTest_block_dispose_0};

void blockTest(a)
{
    __attribute__((__blocks__(byref))) __Block_byref_num_0 num = {(void*)0,(__Block_byref_num_0 *)&num, 0.sizeof(__Block_byref_num_0), 10};
    void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, (__Block_byref_num_0 *)&num, 570425344));
    (num.__forwarding->num) = 20;
    ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_1,(num.__forwarding->num));
}

Copy the code

Wow, sad brother Dei, how come so many things, it doesn’t matter, slowly analysis.

__Block_byref_num_0 *num; __Block_byref_num_0, __blockTest_block_impl_0 has an additional member variable __Block_byref_num_0 *num; Block captures a pointer of type __Block_byref_num_0,

__Block_byref_num_0 we see that __Block_byref_num_0 isa structure and has an isa, so we know that it is really an object. There is also a __forwarding of type __Block_byref_a_0 * and num, which we can guess is used to hold the value of a variable. __forwarding is a bit more complicated, but we’ll get to that later.

__blockTest_block_dispose_0 __blockTest_block_copy_0 and * * * *

__blockTest_block_copy_0 calls _Block_object_assign, and __blockTest_block_dispose_0 calls _Block_object_dispose.

function Call time
__blockTest_block_copy_0 __blockWhen a variable structure instance is copied from the stack to the heap
__blockTest_block_dispose_0 __blockWhen the variable structure instance reference count is 0

More detailed codes for _Block_object_assign and _Block_object_dispose can be viewed in Runtime.c.

BLOCK_FIELD_IS_BYREF we can see that both _Block_object_assign and _Block_object_dispose have a parameter of type 8. What does BLOCK_FIELD_IS_BYREF mean? You can view the following information in block_private. h:

// Runtime support functions used by compiler when generating copy/dispose helpers

// Values for _Block_object_assign() and _Block_object_dispose() parameters
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 the code
  • BLOCK_FIELD_IS_OBJECT: OC Object type
  • BLOCK_FIELD_IS_BLOCK: is a block
  • BLOCK_FIELD_IS_BYREF: being on the stack__blockModified variable
  • BLOCK_FIELD_IS_WEAK:__weakModify the variable only inBlock_byrefUsed when managing internal object memory
  • BLOCK_BYREF_CALLER: handlingBlock_byrefAn extra flag added to internal object memory (telling internal implementations not to retain or copy)

__blockTest_block_desc_0 we can see that it has two more callback Pointers *copy and *dispose, which are assigned to __main_block_copy_0 and __main_block_dispose_0

Finally we see that the call to num looks like this:

__Block_byref_num_0 *num = __cself->num; // bound by ref   

(num->__forwarding->num) = 30;
Copy the code

Here’s why.

Block memory management

__block_impl = _NSConcreteStackBlock; __block_impl = _NSConcreteStackBlock;

type The storage area
_NSConcreteStackBlock The stack
_NSConcreteGlobalBlock Data area
_NSConcreteMallocBlock The heap

We also talked about copy and dispose. In the ARC environment, what are the situations where the compiler automatically copies blocks from stack to heap?

Blocks are copied from the stack to the heap
When a Block’s copy instance method is called
When a Block is returned as a function return value
When passing blocks in Cocoa methods or GCD apis with usingBlocks
When assigning a block to an ID or block type with the __strong modifier

When Bock is copied from stack to heap, __block changes:

  1. whenBlockWhen I’m on the stack,__blockThe storage domain is the stack,__blockVariables are on the stackBlockHold.
  2. whenBlockIs copied to the heap by callingBlockThe inside of thecopyFunction, copy is called internally_Block_object_assignFunction. At this time__blockThe storage domain for variables is the heap,__blockVariables are placed on the heapBlockHold.
  3. When the heapBlockReleased, will callBlockThe inside of thedispose.disposeThe function is called internally_Block_object_dispose, on the heap__blockBe released.

  1. When multiple stacksBlockUse on the stack__blockVariables,__blockVariables are multiple on the stackBlockHold.
  2. whenBlock0When copied to the heap,__blockIt’s going to be copied onto the heap, onto the heapBlock0Hold.Block1It’s still on the stack__block, the original on the stack__blockThe variable__forwardingPoint to after you copy it to the heap__blockThe variable.
  3. whenBlock1Is also copied to the heap when it’s on the heap__blockBe on the heapBlock0andBlock1Only, and__blockReference count of +1.
  4. When the heapBlockAre released,__blockThe variable structure instance reference count is 0, call_Block_object_dispose, on the heap__blockBe released.

The image below depicts the __forwarding change. This explains the existence of __forwarding:

__forwarding ensures that corresponding variables are properly accessed on either the stack or the heap

int main(int argc, char * argv[]) {

    int num = 10;

    NSLog(@"% @",[^{
        NSLog(@"%d",num);
    } class]);

    void (^block)(void) = ^{
        NSLog(@"%d",num);
    };

    NSLog(@"% @",[block class]);
}

Copy the code

Print result:

2019-05-04 18:40:48.470228+0800 BlockTest[35824:16939613] __NSStackBlock__
2019-05-04 18:40:48.470912+0800 BlockTest[35824:16939613] __NSMallocBlock__

Copy the code

We can see that the first Block does not assign to the __strong pointer, and the second Block does not assign to the __strong pointer, so the first is on the stack and the second is on the heap.

Block intercept object

int main(int argc, char * argv[]) {
    {
        Person *person = [[Person alloc] init];
        person.name = @"roy";

        NSLog(@"% @",[^{
            NSLog(@"% @",person.name);
        } class]);
        NSLog(@"% @"The @"+ + + + + + + + + + + + +");
    }
    NSLog(@"% @"The @"-- -- -- -- -- -- -- -- -- -- -- --");
}

Copy the code

Print result:

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end

@implementation Person

- (void)dealloc {
    NSLog(@"-------dealloc-------");
}

@end

typedef void(^Block)(void);

int main(int argc, char * argv[]) {
    {
        Person *person = [[Person alloc] init];
        person.name = @"roy";

        NSLog(@"% @",[^{
            NSLog(@"% @",person.name);
        } class]);
        NSLog(@"% @"The @"+ + + + + + + + + + + + +");
    }
    NSLog(@"% @"The @"-- -- -- -- -- -- -- -- -- -- -- --");
}
Copy the code

We see that when a Block accesses an auto object of object type internally, there is no strong reference to the auto object if the Block is on the stack.

Auto Strong object


typedef void(^Block)(void);

int main(int argc, char * argv[]) {
    Block block;
    {
        Person *person = [[Person alloc] init];
        person.name = @"roy";

        block = ^{
            NSLog(@"% @",person.name);
        };
        person.name = @"david";
        NSLog(@"% @"The @"+ + + + + + + + + + + + +");
    }
    NSLog(@"% @"The @"-- -- -- -- -- -- -- -- -- -- -- --");
    block ();
}

Copy the code

The printed result is

2019-05-04 17:46:27.083280+0800 BlockTest[33745:16864251] +++++++++++++ 2019-05-04 17:46:27.083934+0800 [33745:16864251] ------------ 2019-05-04 17:46:27.084018+0800 BlockTest[33745:16864251] David 2019-05-04 17:46:27. 084158 + 0800 BlockTest (33745-16864251) -- -- -- -- -- -- -- dealloc -- -- -- -- -- -- -Copy the code

/ / macosx-10.13 main.m -fobjc-arc / / macosx-10.13 main.m -fobjc-arc / / macosx-10.13 main.m -fobjc-arc / / Clang rewrites the following code in ARC:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; Person *__strong person; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

We see the Person *__strong Person in __main_block_impl_0; Member variables.

The Block intercepts the auto object, and when the Block is copied to the heap, the Block strongly references the auto object, which explains why the person is out of scope, and the person is not immediately freed, but when the Block is freed, the strong reference to the object is automatically removed, and the object is freed.

Auto Weak object


typedef void(^Block)(void);

int main(int argc, char * argv[]) {
    Block block;
    {
        Person *person = [[Person alloc] init];
        person.name = @"roy";
        __weak Person *weakPerson = person;

        block = ^{
            NSLog(@"% @",weakPerson.name);
        };
        weakPerson.name = @"david";
        NSLog(@"% @"The @"+ + + + + + + + + + + + +");
    }
    NSLog(@"% @"The @"-- -- -- -- -- -- -- -- -- -- -- --");
    block ();
}

Copy the code

The printed result is

2019-05-04 17:49:38.858554+0800 BlockTest[33856:16869229] +++++++++++++ 2019-05-04 17:49:38.859218+0800 BlockTest (33856-16869229) -- -- -- -- -- -- -- dealloc -- -- -- -- -- -- - the 2019-05-04 17:49:38. 859321 + 0800 BlockTest (33856-16869229) -- -- -- -- -- -- -- -- -- -- -- -- The 2019-05-04 17:49:38. 859403 + 0800 BlockTest [33856-16869229] (null)Copy the code

Clang-rewrite-objc main.m cannot create __weak reference because the current deployment target does not support Weak ref error. -fobjc-runtime=macosx-10.13; -fobjc-runtime=macosx-10.13; Clang represents the current runtime environment.

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; Person *__weak weakPerson; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

We see Person *__weak weakPerson in __main_block_impl_0; Member variables.

Conclusion:

  1. whenBlockInternal access to the auto object of object type ifBlockIt is on the stack and will not have a strong reference to the auto object.
  2. Is called if the block is copied to the heapBlockInternal copy, copy is called internally_Block_object_assignThe function,_Block_object_assignThe auto object modifiers (__strong.__weak.__unsafe_unretained) make the corresponding operation when using__strongWill be onpersonThe reference count of the object is incremented by 1 when__weak, the reference count does not change.
  3. ifBlockIf you remove it from the pair, it calls inside the blockdisposeFunction is called internally_Block_object_disposeFunction, which will automatically release the referenceautoObject.

Block loop reference


@interface Person : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, copy) void (^block)(void);

- (void)testReferenceSelf;

@end

@implementation Person

- (void)testReferenceSelf {
    self.block = ^ {
        NSLog(@"self.name = %s", self.name.UTF8String);
    };
    self.block();
}

- (void)dealloc {
    NSLog(@"-------dealloc-------");
}

@end


int main(int argc, char * argv[]) {
    Person *person = [[Person alloc] init];
    person.name = @"roy";
    [person testReferenceSelf];
}

Copy the code

Self. name = Roy. The destructor for Person, dealloc, does not execute. This is a typical circular reference. The code rewritten by Clang looks like this:


struct __Person__testReferenceSelf_block_impl_0 {
  struct __block_impl impl;
  struct __Person__testReferenceSelf_block_desc_0* Desc;
  Person *const __strong self;
  __Person__testReferenceSelf_block_impl_0(void *fp, struct __Person__testReferenceSelf_block_desc_0 *desc, Person *const __strong _self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void _I_Person_testReferenceSelf(Person * self, SEL _cmd) {
    ((void (*)(id, SEL, void(*) ())) (void *)objc_msgSend)((id)self, sel_registerName("setBlock:"), ((void (*)())&__Person__testReferenceSelf_block_impl_0((void *)__Person__testReferenceSelf_block_func_0, &__Person__testReferenceSelf_block_desc_0_DATA, self, 570425344)));
    ((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("block"())); }Copy the code

The testReferenceSelf method in Person has no arguments, but the C++ method has two more arguments: Person *const __strong self; Person *const __strong self; Person *const __strong self; , so we know that the block in Person captures self, the block strongly references self, and self also strongly references the block, thus creating a circular reference.

Weak Unreferences the loop

@implementation Person

- (void)testReferenceSelf {
    __weak typeof(self) weakself = self;
    self.block = ^ {
        __strong typeof(self) strongself = weakself;
        NSLog(@"self.name = %s", strongself.name.UTF8String);
    };
    self.block();
}

- (void)dealloc {
    NSLog(@"-------dealloc-------");
}

@end

Copy the code

Print result:

2019-05-04 19:27:48.27458 +0800 BlockTest[37426:17007507] self. Name = Roy 2019-05-04 19:27:48.275016+0800 BlockTest[37426:17007507] -------dealloc-------Copy the code

We see that the Person object is freed normally, indicating that there is no circular reference. Why? The code rewritten by Clang looks like this:

struct __Person__testReferenceSelf_block_impl_0 { struct __block_impl impl; struct __Person__testReferenceSelf_block_desc_0* Desc; Person *const __weak weakself; __Person__testReferenceSelf_block_impl_0(void *fp, struct __Person__testReferenceSelf_block_desc_0 *desc, Person *const __weak _weakself, int flags=0) : weakself(_weakself) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void _I_Person_testReferenceSelf(Person * self, SEL _cmd) { __attribute__((objc_ownership(weak))) typeof(self) weakself = self; ((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setBlock:"), ((void (*)())&__Person__testReferenceSelf_block_impl_0((void *)__Person__testReferenceSelf_block_func_0, &__Person__testReferenceSelf_block_desc_0_DATA, weakself, 570425344)));
    ((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("block"())); }Copy the code

It can be seen that the WeakSelf member in the __Person__testReferenceSelf_block_impl_0 structure is a __weak modified Person object. That is, __Person__testReferenceSelf_block_impl_0 has a weak dependency on Person. The weak modifier variable is processed in the Runtime. In the Dealloc method of the Person object, the processing method of weak reference is called. Weak_table is used to find the weak_referenced dependent object and clear it.

The last

Ok, that’s all about Block. It took three days of May Day to solve a basic knowledge point. I feel relieved and really tired to write.

(4) The __block modifier of block and its storage domain (3) The interception variable and object of block A brief introduction to the implementation principle of Block and memory characteristics of the third: Copy process analysis of iOS basic principle summary – explore the nature of Block (A)