preface

Every time I improve my knowledge system, I will go back and look at the books I have read, and I will gradually deepen my understanding of it. Reread the principles of Block in Objective-C Advanced Programming for iOS and OS X Multithreading and Memory Management, chapter 2. On the one hand, take notes on yourself and on the other hand, deepen your impression.

directory

  • The essence of the Block
  • Block captures the value of an automatic variable
  • The essence of the __block
  • Block storage domain
  • The __block variable stores fields
  • Intercepted object
  • __block variables and objects
  • Block loop reference

1. Block substance

Block of code:

void (^blk)(void) = ^ {
        printf("Block");
    };
    blk();
Copy the code

Executing the xcrun-sdk iphonesimulator clang-rewrite-objc source file converts the code containing the Block to C++ source code. I am according to the book example, the same conversion of main.m file, after conversion here will be a main. CPP file, open very scary, more than 60,000 lines…

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) {printf("Block");
    }

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, char * argv[]) {

    void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

    return 0;
}
Copy the code

So this is the block that we’ve been using, because it’s all structs and it looks a little abstract, but it’s not that hard to understand.

We’ll start with the __main_block_func_0 function, because all the callbacks we want to perform are written in this function, The anonymous function used by a block (which we define as a block) is actually treated as a simple C function (block__main_block_func_0), whose argument __cself corresponds to the variable self that points to the object itself in the OC instance method, That is, __self is a variable pointing to a Block value. __self, like self in OC, is a pointer to the __main_block_IMPL_0 structure, which is declared as follows:

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

The first variable is the IMPl, which is also a structure and is declared as follows:

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

FuncPrt is a function pointer to the function in the block parenthesis. It is called to execute the function in the block parenthesis. The second member variable is the Desc pointer, and its __main_block_desc_0 structure is declared as follows:

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}
Copy the code

The structure is the size of the area and Block required for future version upgrades. The __main_block_IMPL_0 structure actually ends up like this:

struct __main_block_impl_0 {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
  struct __main_block_desc_0* Desc;
};
Copy the code

We also define a constructor that initializes the structure:

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

This is what the entire __main_block_IMPL_0 structure contains. Now that the initialization function for this structure is defined, taking a closer look at the initialization process, the structure should actually be initialized as follows:

isa = &_NSConcreteStackBlock;
Flags = 0;
Reserved = 0;
FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;
Copy the code

__main_block_func_0 is the function pointer to the implementation of the function mentioned above, which means that the FuncPtr in the structure can be called to our implementation. Where is this constructor initialized? See the source code above when we define a block:

void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
Copy the code

Simplified as:

struct __mian_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *blk = &tmp;
Copy the code

The source code assigns an automatic variable of the __mian_block_IMPL_0 structure type, that is, a pointer to an instance of the __mian_block_IMPL_0 structure generated on the stack, to a variable of the __mian_BLOCK_IMPL_0 structure pointer type, BLK. It sounds a bit convoluted, but what we originally defined as BLK is that the __main_block_IMPL_0 structure pointer points to an instance of the __main_block_IMPL_0 structure.

Let’s look at the construction parameters for the __main_block_IMPL_0 struct instance:

__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
Copy the code

The first argument is a pointer to a C function converted from Block syntax. The second argument is a pointer to an instance of the __main_block_desc_0 structure initialized as a static global variable.

static struct __main_block_desc_0 __main_block_desc_0_DATA = { 
    0, 
    sizeof(struct __main_block_impl_0)
};
Copy the code

That is the size of an instance of the __main_block_IMPL_0 structure.

Let’s look at how the __main_block_IMPL_0 struct instance (that is, Block) on the stack is initialized with these parameters. This is the implementation of BLK () :

((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
Copy the code

Simplify the following:

(* series - > impl. FuncPtr) (series);Copy the code

FuncPtr is the function pointer we passed in when we initialized an instance of the __main_block_desc_0 structure, used here to call the function, as we just said, The pointer to the __main_block_func_0 function with block syntax conversions is assigned to the member variable FuncPtr. BLK is also passed as a parameter, which was originally __cself. This completes the initialization and invocation of the block.

2.Block captures the value of an automatic variable

Add an additional local variable val based on the above example:

 int val = 0;
 void (^blk)(void) = ^{
    NSLog(@"%d",val);
  };
        
 val = 10;
 blk();
        
Copy the code

The C++ code is as follows:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int val; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int flags=0) : val(_val) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int val = __cself->val; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_p6_239crx8x16s8vby9bfclq5d40000gn_T_main_057be8_mi_0,val); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; int main(int argc, char * argv[]) { int val = 0; void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val)); val = 10; ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); }Copy the code

This is slightly different from the code transformed in the previous section, where the automatic variable is appended as a member variable to the __main_block_IMPL_0 structure. The types of member variables declared in the __main_block_IMPL_0 structure are exactly the same as those of automatic variables (unused automatic variables in block syntax expressions are not appended, that is, they are not captured if they are not used within the block).

The __main_block_impl_0 constructor also differs from the previous one:

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

When initializing an instance of the __main_block_IMPL_0 structure, the automatic variable val is passed into the structure as a parameter. That is, when we define a block, the captured automatic variable is used to initialize the structure:

void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val));
Copy the code

Blocks with automatic variables like this are actually initialized like this:

impl.isa = &_NSConcreteStackBlock;
impl.Flags = 0;
impl.FuncPtr = __main_block_func_0;
Desc = &__main_block_desc_0_DATA;
val = 0;
Copy the code

As you can see, when the __main_block_IMPL_0 structure is initialized, the value of the variable val is captured and assigned to the _val member of the __main_block_IMPL_0 structure. This is a value capture, not a memory address, so we cannot change it externally.

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int val = __cself->val; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_p6_239crx8x16s8vby9bfclq5d40000gn_T_main_057be8_mi_0,val);
        }
Copy the code

__cself->val refers to this block object, __cself->val is the __main_block_IMPL_0 member variable _val, and the value of the automatic variable is assigned to _val, So if we change the value of an automatic variable outside the block, it doesn’t work inside the block.

3. The essence of the __block

If we want to change the values of automatic variables, static global variables, global variables, and static variables captured by the block, they are not captured in the block, so they can be overwritten inside the block. So what do they do at the code level? Look at the source code using the command above:

int global_var = 1;
static int static_global_var = 2;

int main(int argc, char * argv[]) {
    static int static_var = 3;
    void (^blk)(void) = ^{
        global_var *= 1;
        static_global_var *= 2;
        static_var *= 3;
    };
    
    blk();
    
    return 0;
}
Copy the code

After the conversion of the source code:

int global_var = 1; static int static_global_var = 2; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *static_var; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_var, int flags=0) : static_var(_static_var) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *static_var = __cself->static_var; // bound by copy global_var *= 1; static_global_var *= 2; (*static_var) *= 3; } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; int main(int argc, char * argv[]) { static int static_var = 3; void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_var)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;
}
Copy the code

Access to static global variables static_global_var and global_var is exactly the same as before the conversion.

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_var = __cself->static_var; // bound by copy

        global_var *= 1;
        static_global_var *= 2;
        (*static_var) *= 3;
    }
Copy the code

Int *static_var = __cself->static_var passes a pointer to the static local variable to the __main_block_IMPL_0 structure. This is also a way to use a variable that is out of scope. That’s access through a pointer. So why don’t we do this with local variables? We’ll talk about that later. We just need to know that static local variables can change their value inside a block. Returning to the subject, how do we change the value of an automatic variable, which is modified by __block, look at the code:

int main(int argc, char * argv[]) {
    __block int var = 10;
    void (^blk)(void) = ^{
        var = 1;
    };
    
    blk();
    
    return 0;
}

Copy the code

The conversion is as follows:

struct __Block_byref_var_0 { void *__isa; __Block_byref_var_0 *__forwarding; int __flags; int __size; int var; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_var_0 *var; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_var_0 *_var, int flags=0) : var(_var->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_var_0 *var = __cself->var; // bound by ref (var->__forwarding->var) = 1; } static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->var, (void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/); } static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->var, 8/*BLOCK_FIELD_IS_BYREF*/); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*); void (*dispose)(struct __main_block_impl_0*); } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0}; int main(int argc, char * argv[]) { __attribute__((__blocks__(byref))) __Block_byref_var_0 var = {(void*)0,(__Block_byref_var_0 *)&var, 0, sizeof(__Block_byref_var_0), 10}; void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_var_0 *)&var, 570425344)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);return 0;
}
Copy the code

It is not hard to find an additional instance of the __Block_byref_var_0 structure, which is also an implementation of __block. The member variable var in this structure is equivalent to the member variable of the automatic variable outside the block. Let’s look at how the block assigns to the member variable:

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_var_0 *var = __cself->var; // bound by ref

        (var->__forwarding->var) = 1;
    }
Copy the code

A pointer to a static variable in a block is used when assigning a value to that static variable, but when assigning a value to __block, You can actually see in the __Block_byref_var_0 structure that __block has a member variable __Block_byref_var_0 *__forwarding, which is a pointer to the instance itself, The member variable __forwarding is used to access its own var, so why is it used to access the member variable __forwarding to itself? As we’ll see in the next section, this is how it accesses the automatic variable. The reason we actually access var is that when we define the automatic variable as a __block, we initialize a structure of type __Block_byref_var_0, which is initialized to 10 by default (because we initialized var with 10). Equivalent to holding a member variable of the original automatic variable. The __main_block_IMPL_0 structure is then passed as a block when initialized, so __cself->var is actually the structure of the initialized block. Var ->__forwarding->var is the __forwarding member that accesses the block structure, and the __forwarding member refers to itself. So __forwarding->var returns its own member variable var, so the whole process is complete.

4. Block storage domain

From the above analysis, there are several problems that need to be explained:

1. Why is there _forwarding? (Later)

2.BLock scope is on the stack. If a variable is out of scope, it will be destroyed.

Both blocks and __blocks analyzed above are struct type automatic variables that are generated on the stack and called “stack blocks.” There are actually two types of blocks, “heap blocks” and “global blocks.” Global blocks, like global variables, are set in the program’s. Data data area. Heap blocks, as the name implies, are allocated on the heap, and are of the following types:

  • _NSConcreteGlobalBlock
  • _NSConcreteStackBlock
  • _NSConcreteMallocBlock

There are two cases that are assigned to data regions by default:

  • 1. Block syntax is used to describe global variables.
  • 2. Block syntax expressions do not use captured values of automatic variables.

The other Block syntax generates blocks for the _NSConcreteStackBlock class object set on the stack. Blocks configured on global variables can also be safely used by Pointers from outside the scope of variables, but blocks placed on the stack are discarded if the scope of the variable to which they belong ends. Since __block is also configured on the stack, the same __block variable is also deprecated. Blocks provides a way to solve this problem by copying Blocks and __block variables from the stack to the heap. This allows blocks on the heap to continue to exist even after the variable scope described by block syntax ends. In some cases, the compiler generates a copy of the stack block to the heap by default. In most cases, the compiler makes an appropriate determination to copy stacks onto the heap, except for one case:

  • Pass blocks to arguments of a method or function.

If a block is passed as an argument, you need to copy it manually. The compiler does not copy it automatically. However, there are two cases where we don’t need to implement it manually because copy is already implemented inside the function:

  • The Cocoa framework’s method cut methods contain usingBlock.
  • The GCD API.

For example, the manual implementation of the example in the book:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    id object = [self getBlockArray];
    typedef void(^blk_t)(void);
    blk_t blk = (blk_t)[object objectAtIndex:0];
    blk();
}

- (id)getBlockArray {
    int var = 10;
    return [[NSArray alloc] initWithObjects:
            ^{NSLog(@"blk0:%d",var); }, ^{NSLog(@"blk1:%d",var); }, nil]; }Copy the code

The program crashed while executing BLK (). The compiler does not automatically copy the block, since the block created on the stack is discarded when the getBlockArray function ends. The book makes it clear why the compiler does not copy all blocks to the heap: copying blocks from the stack to the heap is CPU intensive, and copying blocks to the heap is a waste of CPU resources when blocks are available on the stack. So copy the block in this case:

- (id)getBlockArray {
    int var = 10;
    return [[NSArray alloc] initWithObjects:
            [^{NSLog(@"blk0:%d",var); } copy], [^{NSLog(@"blk1:%d",var); } copy], nil]; }Copy the code
2018-12-24 12:33:33.526163+0800 Blocks- Capture the value of variable [54592:3223484] blK0:10Copy the code

In ARC, there is no need to worry about memory problems caused by multiple copies.

There is one more problem with _forwarding. At least now we know that the block we set on the stack is copied to the heap, so we don’t need to worry about it going out of scope and being released. So _forwarding continues.

5.__block variable storage domain

If a __block variable is used in a Block, the __block used when the Block is copied from the stack to the heap is also copied to the heap and is held by the Block. Each Block that uses the current __block variable is copied to the heap with the __block reference count +1. If the Block configured on the heap is deprecated, the corresponding __block reference count is -1 until all blocks are freed and the __block is freed.

That is, with the exception of the two cases mentioned above, the rest of our blocks are basically copied to the heap, which means that __blocks we use are also copied to the heap, like OC objects, with reference counts. When __block is copied to the heap, the _forwarding member in the __block structure on the stack refers to the __block instance in the heap. The _forwarding of the __block variable above the heap points to itself. It looks like this as follows:

(var->__forwarding->var) = 1; (var->__forwarding->var) = 1; Var ->__forwarding actually accesses the __block structure on the heap. Var ->__forwarding->var is the var member of the heap. So even the stack above the __block was released, we can also visit the inside of the heap var, which is why the automatic variables don’t like the static static variables through a pointer to access, because automatic variables will be released at the end of the scope, copy to the heap, scope end of pile top will have its corresponding copy, The copy is released only after the Block that used it is released.

6. Intercept objects

In fact, for object types, Block captures them very much like __block. What happens when a Block and a __block are copied to the heap and released from the heap has not been discussed in detail before, but it is also possible to summarize and think through the process of capturing objects. Take a look at the sample code above:

typedef void(^blk_t)(id);
    blk_t blk;
    
    {
        id array = [[NSMutableArray alloc] init];
        blk = [^(id obj){
            [array addObject:obj];
            NSLog(@"array count : %ld",[array count]);
        } copy];
    }
    
    blk([[NSObject alloc] init]);
    blk([[NSObject alloc] init]);
    blk([[NSObject alloc] init]);
Copy the code
2018-12-25 12:25:06.678625+0800 Blocks- Capture variable value [56349:3341197] array count: 1 2018-12-25 12:25:06.679199+0800 Blocks- capture variable value [56349:3341197] array count: 2018-12-25 12:25:06.679210+0800 Blocks- Capture variable value [56349:3341197] array count :3Copy the code

Array is normally discarded when it goes out of scope, but the print looks fine. That is, array persists beyond the scope of the variable. The source code through conversion is as follows:

Block structure part:

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; id array; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id _array, int flags=0) : array(_array) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj) { id array = __cself->array; // bound by copy ((void (*)(id, SEL, ObjectType _Nonnull))(void *)objc_msgSend)((id)array, sel_registerName("addObject:"), (id)obj); NSLog((NSString *)&__NSConstantStringImpl__var_folders_p6_239crx8x16s8vby9bfclq5d40000gn_T_main_1dc794_mi_0,((NSUInteger  (*)(id, SEL))(void *)objc_msgSend)((id)array, sel_registerName("count")));
        }
        
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
Copy the code

Using the Block section:

typedef void(*blk_t)(id);
    blk_t blk;

    {
        id array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));
        blk = (blk_t)((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)(id))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, array, 570425344)), sel_registerName("copy"));
    }

    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")));
    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")));
    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init")));
Copy the code

I tested this one on my own and had a little doubt. There is a __strong modifier in front of array according to the example in the book, but I don’t see the __strong modifier in my source code. If weak is not used, the default value is strong. I’m going to default to the id array being modified by __strong. C constructs cannot have variables with __strong modifiers, because the compiler does not know when C constructs should be initialized and discarded, and cannot manage memory well. However, it is good at timing when blocks are copied from the stack to the heap and discarded from the heap, so even variables that contain the OC modifier in the Block structure can be discarded after the Block is discarded.

Struct __main_block_desc_0; struct __main_block_desc_0; struct __main_block_desc_0; struct __main_block_desc_0; struct __main_block_desc_0;

  • void (copy)(struct __main_block_impl_0, struct __main_block_impl_0*);
  • void (dispose)(struct __main_block_impl_0);

This is actually in the analysis of the __block principle when there is, in fact, they are used for the same purpose, are used to manage Block memory.

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
Copy the code

The _Block_object_assign function is equivalent to calling the retain function, which assigns the object to a structure member variable of the object type.

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
Copy the code

The _Block_object_dispose function is equivalent to a call to the release instance method that releases an object assigned to a structure member variable of the object type. But according to the source of the transformation, neither __main_block_copy_0 nor __main_block_dispose_0 function Pointers are called, so when are they triggered?

  • Copy is triggered when blocks on the stack are copied to the heap.
  • Dispose function is triggered when a Block on the heap is discarded.

Dispose method is called when a Block is disposed without strong references. So when does a Block on the stack copy to the heap?

  • 1. Call Block’s copy instance method.
  • 2. When Block is returned as a function return value.
  • 3. When assigning a Block to a class or a Block member variable with the __strong modifier ID type.
  • 4. When passing blocks in Cocoa framework methods or GCD apis that have usingBlocks in their method names.

This allows automatic variables, which are modified with the __strong modifier, to be used outside the scope.

In fact, these two functions are already used when using __block, with slight differences:

  • When intercepting objects –>BLOCK_FIELD_IS_OBJECT
  • __block variable –>BLOCK_FIELD_IS_BYREF

These two parameters are used to distinguish whether a Block captures an object type or a __block variable. In addition, copy and dispose are the same, they are both held and released by the Block.

__block variables and objects

The __block specifier can specify any type of automatic variable. The __block specifier can specify any type of automatic variable. The __block specifier can specify any type of automatic variable. Including, of course, object types. Again, look at the code from the example in the book:

__block id obj = [[NSObject alloc] init];
Copy the code

Synonymous with

__block id __strong obj = [[NSObject alloc] init];
Copy the code

The clang conversion is as follows:

Struct __Block_byref_obj_0 {void *__isa; __Block_byref_obj_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); id obj; }; 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
/ * __block variable declaration section * / __attribute__ ((__blocks__ (byref))) __Block_byref_obj_0 obj = {(void *) 0, (__Block_byref_obj_0 *) & obj, 33554432, sizeof(__Block_byref_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};Copy the code

Here are the _Block_object_assign and _Block_object_dispose functions described in the previous section. In fact, the compiler defaults this obj type to __strong. When blocks are copied from the stack to the heap, the _Block_object_assign function holds objects that the Block intercepts. When blocks on the heap are discarded, Use the _Block_object_dispose function to dispose the objects intercepted by the Block. This means that __strong objects that use __block modifier will continue to be held when a __block variable is copied from the stack to the heap and continues to exist on the heap. This is the same as for objects in blocks that use automatic variables assigned to object types with the __strong modifier attached.

Instead of __strong, what about __weak?

__weak objects are freed even when __block is used, and in fact the source code in the book tells us that this is the case, but the object is automatically set to nil. However, use the __unsafe_unretained modifier, beware of the wild pointer problem.

8.Block circular reference

Avoid recurring references. Depending on the purpose of the Block, you can use the __block variable, __weak modifier, and __unsafe_unretained modifier to avoid recurring references.

__weak and __unsafe_unretained weak references do not worry about releasing.

A __block modifier, which is a strong reference, needs to set nil to the modified object within the Block. Blocks must be executed to avoid circular references, but the __block variable controls how long the object is held.