@[toc]

Block profile specification

What is Blocks?

  • In a nutshell: anonymous functions with automatic variables (local variables)

As the name implies, a Block has no function name. In addition, a Block is marked with the ^, which makes it easy to find the Block

  • Blocks are also called closures, blocks of code. That is, we can encapsulate the code we want to execute in this block of code and call it directly when we need it
  • Block shares locally scoped data. If a method is implemented and the method defines a block, the block has access to the method’s local variables and parameters (including stack variables), as well as functions and global variables (including instance variables). This access is read-only, and if we declare a variable with the __block modifier, we can change its value within the Block. We’ll talk about that later

Block syntax

format

A standard format

like:

^int (int count){return count + 1; }Copy the code

Omit the format

The default return value type is void

If it’s a void we can omit (void) by default

like:

^ {return count + 1; }Copy the code

Block variables

  • A Block variable is like a function pointer
  • Declaring a Block type variable simply changes the “*” of a function pointer type variable to “^”.
int (^blk)(int)
Copy the code

Block variables are identical to C variables and can be used for the following purposes:

  • Automatic variables
  • Function parameters
  • A static variable
  • Static global variable
  • The global variable

Intercept automatic variables

Defined: with an automatic variable value in the Block as “capture automatic variable value”.

int dmy = 256;
int val = 10;
void (^blk)(void) = ^{
	printf("val = %d\n",val);
};
val = 2;
blk();
return 0;
Copy the code

You can see that val = 10 hasn’t changed

That is to say, the variable is intercepted when the code runs to the definition of the block, when the execution is not the original variable

__block specifier

A block can intercept a variable, but it cannot change the value of a variable in a block

In this case, we use the __block modifier to modify the variable. If we want to copy the variable within the block, we use the modifier to ensure that the variable can be assigned

int main(int argc, const char * argv[]) {
   __block int val = 10;
    void (^blk)(void) = ^ {// val = 5;
        printf("val = %d\n",val);
    };
    val = 2;
    blk();
    return 0;
}
Copy the code

So this code right over here is going to change to a 2 and then it’s going to change to a 5 if you assign it internally

Intercepted automatic variables

The book doesn’t say anything useful about this, but one thing to note is, what about the current Blocks, the method of intercepting automatic variables doesn’t implement the interception of C arrays which means

const char text[] = "hello";
void(^blk)(void) = ^{
	printf("%c\n",text[2]);
};
Copy the code

complainsHowever, it’s ok to use Pointers to represent the array

Circular reference to Block

- (id)init {
	self = [super init];
	blk_ = ^{NSLog(@"self = %@".self); };return self;
}
Copy the code

When we initialize an instance of this class, we create a circular reference, because the Block syntax is assigned to the member variable BLK, so the Block generated on the stack by the Block syntax is copied from the stack to the heap and holds self. Self holds the Block, the Block holds self, and it’s looping.

Using the __block variable avoids the advantages of circular references:

  • The __block variable allows you to control how much space an object holds

Disadvantages:

  • Blocks must be executed to avoid circular references

The realization of the Block

The implementation of a Block is based on Pointers and function Pointers, and the Block property is a pointer to a structure.

Convert c++ source code and then analyze

Normal Block flow

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

The source code section

int main(int argc, const char * argv[]) {
    void(*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
    return 0;
}
Copy the code

The ticket! The block syntax becomes &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_data)); The procedure for calling a block is myBlock->FuncPtr(myBlock), so let’s look at the corresponding parts involved

Initialize the Block section

/ / Block structure
struct __main_block_impl_0 {
  struct __block_impl impl;//impl: the actual function pointer to the Block, which is the __main_block_func_0 structure that contains the body of the Block
  struct __main_block_desc_0* Desc;// The Desc pointer points to the __main_block_desc_0 () structure that contains additional information about the Block
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {// __main_block_IMPL_0: Block constructorimpl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

So we can see that the __main_block_impl_0 structure, which is the ==Block structure ==, contains three parts:

  1. Member variable impl;
  2. Member variable Desc pointer;
  3. The __main_block_IMPL_0 constructor

Struct __block_impl structure

A structure containing a pointer to the actual function of the Block

struct __block_impl {
  void *isa;	// Hold an instance pointer to the Block structure
  int Flags;	/ / sign
  int Reserved;	// The area size required for future updates
  void *FuncPtr;	// Function pointer
};
Copy the code
  • __block_implContains Pointers to the actual function of the BlockFuncPtr.FuncPtrThe pointer points to the body of the Block, which is in the OC code for the Block^ {... }Part of the
  • It also includes Flags, which may be used when implementing internal operations on blocks
  • The area size required for future upgrades is Reserved
  • An instance pointer isa to the __block_impl structure

struct __main_block_desc_0

Block Additional information structure: contains the size of the area and the size of the Block required for future version upgrades

static struct __main_block_desc_0 {
  size_t reserved;	// The area size required for future updates
  size_t Block_size;	/ / Block size
} __main_block_desc_0_DATA = { 0.sizeof(struct __main_block_impl_0)};
Copy the code

The __main_block_IMPL_0 constructor is a constructor and initializes the members of the __main_block_IMPL_0 structure (the Block 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

Constructor assignment to individual member variables let’s go back to where we just turned it into C++

struct __main_block_impl_0 temp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 *myBlock = &temp;
Copy the code

This code uses the __main_block_IMPL_0 constructor to generate a pointer to an instance of the __main_block_IMPL_0 struct type, The pointer variable myBlock is assigned to the __main_block_IMPL_0 structure (Block structure) type

As you can see, the Block constructor is called with two arguments

The first argument: __main_block_func_0

  1. This is the body of the Block syntax for a Block. Take a look at the definition of the __main_block_func_0 structure in the underlying c++ section
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        printf("myBlock");
    }
Copy the code

Same as our code block in OC

The argument __cself is a pointer variable to the value of the Block, which is equivalent to the second argument in OC: __main_block_desc_0_DATA contains information about the Block

Call part

So, the Block structure and the member variables and that’s done let’s see how do we call blocks in main

((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
Copy the code
  • The first member of the myBlock structure is __block_impl, so the first address of myBlock is__block_impl implThis means that our first cast can be directly cast to __block_impl
  • ((void (*)(__block_impl *))Is the type of Func in __block_impl
  • ((__block_impl *)myBlock)->FuncPtr)Call a function
  • ((__block_impl *)myBlock);Function parameters

What I mean by this call argument is that I pass itself to the argument __cself to func_0, which shows that Block is being passed as an argument.

Substantive summary of Block

In a nutshell, a Block is an object (its first member is an ISA pointer)

In the constructor, we can see that imp. isa = &_nsConcretestackBlock; We’re assigning to the ISA pointer, and _NSConcreteStackBlock is the parent of this block instance. When the Block is called as an OC object, information about the class is placed in the _NSConcretestackBlock

  • In the case of an object, its first internal member is an ISA pointer;
  • In the case of a function call that is actually encapsulated: a Block of code inside a Block encapsulates a function call, and calling a Block calls the encapsulated function
  • For the execution context, the description of the Block Desc, the description object contains the Block information and the memory-related function that captures the variable, and the context in which the Block is located?

The type of the Block

/ / MRC environment
int age = 10;
        void (^block)(void) = ^ {NSLog(@"%d %d", height, age);
        };
        
        NSLog(@"%@\n %@\n %@\n",
              [block class],
              [[block class] superclass],
              [[[block class] superclass] superclass]);
        
        return 0;
Copy the code

The corresponding result is __NSStackBlock __ NSBlock NSObject which means that this Block up here is of type NSStackBlock and it ultimately inherits from NSObject which means that the Block is essentially an OC object

There are three types of blocks

  • NSMallocBlock(_NSConcreteMallocBlock) Objects are stored in the heap
  • NSStackBlock(_NSConcreteStackBlock) Objects are stored in stacks
  • NSGlobalBlock(_NSConcreteGlobalBlock) Objects are stored in the data area

In simple terms

  • It captures the automatic variable which is the Block which is the stack type
  • No capture is a data area
  • You don’t have a Block that’s created in the heap, so the heap area means the same thing as autoRelease. A Block of type Stack is copied into the heap area

Look at the following three scenarios in detail

NSGlobalBlock

  1. Using blocks where Global variables are described defaults to Global

  1. Expressions in the Block syntax do not intercept automatic variables

NSStackBlock

In all cases other than that, the Block that’s created is an NSConcreteStackBlock object

It’s stored in the stack. If the scope of the variable to which the Block belongs ends, the Block is deprecated, and if the __block variable is used, the __block variable is also deprecated

NSMallocBlock

There are no stack blocks under ARC. Because blocks are automatically copied into heap blocks

To solve the problem of blocks on the stack being discarded at the end of variable scope, blocks provide the ability to copy Block objects and __block variables from the stack to the heap.

So the Block and __block variables on the heap can continue to exist and be used even after the stack scope ends

That’s the graph in the book

Block as an attribute, what keyword is used to decorate it?

Under MRC: Use copy. Since the block declaration is in the stack, using the copy decoration copies the block from the stack to the heap.

Arc: Use copy or strong. Because even if you use strong, it will automatically copy, copy the block from the stack to the heap.

Copy of

In the ARC environment, the compiler will determine, and all three cases will be copied automatically

  • Block is returned as a function return value
  • Pass a Block to a method or function, using the following two methods
    • Cocoa framework method with a method nameusingBlockWhen etc.
    • The GCD API
  • When a Block is assigned to a member variable of type ID or Block with the __strong modifier

Of course there’s automatic copying and we can copy it manually

Our manual copy has different effects for different Block classes

  • Stack -> Copy from stack to heap
  • Data area -> no change
  • Heap area -> reference count increased

The __block variable is also affected when the Block of the __block variable is copied from the stack to the heap

In accordance with the oc’s memory management mechanism, the relationship between the two changes from a block using __block to a block owning __block

Block intercepts variables

We’ve seen in ARC that there are several modifiers for variables, so let’s see how does a Block capture variables of different modifiers

Let’s just throw a conclusion and use it later

Global variable: Do not capture local variable: Capture value Static global variable: do not capture static local variable: capture pointer

Let’s start with the normal ones

Examples of local variables

int c = 30;
int main() {
    int a = 10, b = 20;
    void(^myLocalBlock)(void) = ^ {NSLog(@"%d\n%d\n%d\n",a,b,c);
    };
    void(^Block)(int.int.int) = ^ (int a, int b, int c) {
        NSLog(@"%d\n%d\n%d\n",a,b,c);
    };
    a = 40;
    b = 50;
    myLocalBlock();
    Block(a,b,c);
    return 0; } Direct access is caught and indirect access is notCopy the code

The Block expression intercepts the value of the local variable used and holds the instantaneous value of that variable.

The interception of automatic variables in a Block only applies to the automatic variables used in the Block

Access local variables directly

With a question in mind, why intercept only transient values and not the current values of local variables? Take a look at the corresponding c++ source code

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  int b;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int _b, int flags=0) : a(_a), b(_b) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy
  int b = __cself->b; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_pt_w7b9v92s197g97khtskqdnn40000gn_T_main_1f40e5_mi_0,a,b,c);
    }

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 a = 10, b = 20;
    void(*myLocalBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, b));
    a = 40;
    b = 50;
    ((void (*)(__block_impl *))((__block_impl *)myLocalBlock)->FuncPtr)((__block_impl *)myLocalBlock);
    return 0;
}
Copy the code
  • You can see__main_block_impl_0The structure has two more member variables, a and b, whose values come from__main_block_impl_0The value passed in the constructor

The constructor plus the colon is equivalent to an assignment

__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int _b, int flags=0) : a(_a), b(_b) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
Copy the code
  • in__main_block_func_0In the structure, variables a and b use the values obtained by __cself

A = __cself – > a namely; b = __cself->b; This also shows that a and B are only variables inside the Block. Changing the value of a local variable outside the Block does not change the value of a variable inside the Block

  • We can also see that global variables are not stored in the structure of the Block, but are called directly when called

A little summary of the process

The source code for the Block syntax part is to call the constructor of __main_block_IMPL_0. The constructor normally takes two parameters, func_0 and desc_0. __main_block_IMPL_0 also adds two member variables, a and b, to which the constructor will automatically assign values. When func_0 is called, a and b are passed directly through __cself (which is equivalent to self and passes itself as arguments) (in this case a and b are members because the constructor has been assigned).

Local variables are accessed indirectly by passing values

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, int a, int b, int c) {

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_pt_w7b9v92s197g97khtskqdnn40000gn_T_main_ce211a_mi_0,a,b,c);
    }

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 a = 10, b = 20;
    void(*Block)(int.int.int) = ((void(*) (int.int.int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    a = 40;
    b = 50;
    ((void (*)(__block_impl *, int.int.int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, a, b, c);
    return 0;
}
Copy the code
  • __main_block_impl_1The structure does not have variables a and b, indicating that the variables are not stored in the Block structure by passing values directly.
  • Func_0, you pass in a,b directly through the argument list, and you pass in the values of a,b, and c when you call the function instead of calling the local variables directly.

Static modifies variables

static int c = 30;
int main() {
    static const int a = 10;
    static int b = 20;
      void (^Block)(void) = ^{
        printf("a = %d, b = %d, c = %d\n",a, b, c);
    };
    b = 100;
    c = 100;
      Block();                 A = 10, b = 100, c = 100
}
Copy the code

As we can see from the demo above, Block objects do not capture static global and static local variables

Again, go through the source code

static int c = 30;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const int *a;
  int *b;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const int *_a, int *_b, int flags=0) : a(_a), b(_b) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  const int *a = __cself->a; // bound by copy
  int *b = __cself->b; // bound by copy

        printf("a = %d, b = %d, c = %d\n",(*a), (*b), c);
    }

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() {
    static const int a = 10;
    static int b = 20;
      void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &a, &b));
    b = 100;
    c = 100;
      ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}
Copy the code
  • According to the same__main_block_impl_0Structure, static local variablesstatic int bAdd as a member variable in the form of a pointer, while static local variablesstatic const int aIn order toconst int *As a member variable. Global static variables are not added as member variables
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const int *a;
  int *b;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const int *_a, int *_b, int flags=0) : a(_a), b(_b) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
  • Looking at the func_0 structure, static global variables are accessed directly

Static local variables and static local constants are passed through Pointers, but const cannot be assigned

Why can we get the latest value of static variable?

  1. Static, stored in the global store, the address will remain unchanged throughout the program, so the latest value can be accessed.
  2. When static is modified, global variables are accessed directly and local variables are accessed (as long as they contain static variables).

Const modifiers

const int c = 30;
int main() {
    const int a = 10;
    int b = 20;
      void (^Block)(void) = ^{
        printf("a = %d, b = %d, c = %d\n",a, b, c);
    };
      Block();                 A = 10, b = 50, c = 60
}
Copy the code
const int c = 30;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const int a;
  int b;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const int _a, int _b, int flags=0) : a(_a), b(_b) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  const int a = __cself->a; // bound by copy
  int b = __cself->b; // bound by copy

        printf("a = %d, b = %d, c = %d\n",a, b, c);
    }

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() {
    const int a = 10;
    int b = 20;
      void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, b));
      ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}
Copy the code

You can see

  • A const global variable is still directly accessible
  • Const local variables are still value-passed, just like normal automatic variables

conclusion

Global variables: Do not capture local variables: Capture values Static global variables: Do not capture static local variables: Capture const pointer modifiers are the same as local variables

Block Intercepts objects

int main() {		
    id obj = [NSMutableArray array];
      void (^Block)(void) = ^ {NSLog(@ "% @",obj);
    };
    [obj addObject:@1];
      Block();
}
// The result is 1
Copy the code
#import "Person.h"
int main() {
    Person *person = [[Person alloc]init];
    person.age = 20;
      void (^Block)(void) = ^{
          printf("%d",person.age);
    };
    person.age = 30;
      Block();
}
// The output is 30

Copy the code
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __strong id obj;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __strong id _obj, int flags=0) : obj(_obj) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __strong id obj = __cself->obj; // bound by copy


          NSLog((NSString *)&__NSConstantStringImpl__var_folders_pt_w7b9v92s197g97khtskqdnn40000gn_T_main_09fb31_mi_0,obj);
    }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); }static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->obj, 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};
int main() {
    id obj = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));


      void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, 570425344));

    ((void(*) (id, SEL, ObjectType  _Nonnull __strong(a))void *)objc_msgSend)((id)obj, sel_registerName("addObject:"), (id _Nonnull)((NSNumber *(*)(Class, SEL, int(a))void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 1));
    
      ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}
Copy the code
  • We see__main_block_impl_0There is an extra member variable in the structure __strong id obj;Because obj is an automatic variable, the automatic variable obj is captured here as_main_block_impl_0Member variables of the structure.
  • Fun0 is a pointer intercept, func_0 is a pointer intercept, func_0 is a pointer intercept, func_0 is a pointer intercept__strong id obj = __cself->obj; Person *__strong person = __cself->person;The operation of such an assignment
  • It also adds two function Pointers
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); }static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); }Copy the code

For these two function Pointers

  • __main_block_copy_0The function is to call_Block_object_assign, equivalent to retain, assigns the object to a struct variable of the object type__main_block_impl_0In the. Called when a Block on the stack is copied to the heap.
  • __main_block_dispose_0call_Block_object_dispose, equivalent to release, which frees an object assigned to a struct variable of object type. Called when a Block is discarded on the heap.

__block qualifier

__block modifies local variables

int main() {
    __block int a = 10, b = 20;
      void (^Block)(void) = ^{
          printf("%d \n%d \n", a, b);
    };
    a = 100;
    b = 100;
      Block();
}
/ / 100 100
Copy the code
struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};
struct __Block_byref_b_1 {
  void *__isa;
__Block_byref_b_1 *__forwarding;
 int __flags;
 int __size;
 int b;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __Block_byref_b_1 *b; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, __Block_byref_b_1 *_b, int flags=0) : a(_a->__forwarding), b(_b->__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_a_0 *a = __cself->a; // bound by ref
  __Block_byref_b_1 *b = __cself->b; // bound by ref

          printf("%d \n%d \n", (a->__forwarding->a), (b->__forwarding->b));

    }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/); _Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/); }static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/); _Block_object_dispose((void*)src->b, 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() {
    __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0.sizeof(__Block_byref_a_0), 10};
__Block_byref_b_1 b = {(void*)0,(__Block_byref_b_1 *)&b, 0.sizeof(__Block_byref_b_1), 20};
      void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, (__Block_byref_b_1 *)&b, 570425344));
    (a.__forwarding->a) = 100;
    (b.__forwarding->b) = 100;
      ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}
Copy the code

The ticket!

I just added an __block modifier and that’s it

Let’s see how that works

Let’s start with __main_block_IMPL_0

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_a_0 *a; // by ref
  __Block_byref_b_1 *b; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, __Block_byref_b_1 *_b, int flags=0) : a(_a->__forwarding), b(_b->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code
  • __block int a __block int bThey become__Block_byref_a_0 *a; __Block_byref_b_1 *b; Pointer to a struct type (why a struct pointer, because there may be more than one variable modified by __block?) A structure is generated for __block variables, whether they are used or not

Take any of these constructs for example (except for member variables)

struct __Block_byref_a_0 {
  void *__isa; // Identifies the ISA instance variable of the object class
__Block_byref_a_0 *__forwarding; // Pass in the address of the variable
 int __flags; / / sign
 int __size; // The size of the structure
 int a; // Store the actual value of a, as before without the __block modifier
};
Copy the code

Look at the assignment of these two in main

a = {
void *__isa = 0.// For c basic data types it will be 0 because they are not objects and have no class
__Block_byref_a_0 *__forwarding = &a, 
 int __flags = 0.int __size = sizeof(__Block_byref_a_0),
 int a = 10;
};
Copy the code

You can see that the __isa pointer is null, __forwarding refers to the address of the local variable a itself, __flags is assigned 0, __size is the size of the structure, and a is assigned 10.So __forwarding is actually the address of the local variable a itself.

We can see that in main,

    (a.__forwarding->a) = 100;
    (b.__forwarding->b) = 100;
Copy the code

So a variable decorated with __block changes the value in the body of the Block by passing Pointers.

So we have our __block both on the heap and on the stack, how do we find the one we need?

This uses the __forwarding pointer, which simply refers to itself when it is not copied, and when copied, refers to the __block variable on the heap

It’s not held on the stack, it’s just held when it’s copied to the heap and it’s called copy

__block modifies an object

int main() {
    __block Person *person = [[Person alloc]init];
    void (^Block)(void) = ^{
          person = [[Person alloc]init];
          NSLog(@ "% @",person);
      };
    Block();
}
Copy the code
struct __Block_byref_person_0 {
  void *__isa;
__Block_byref_person_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__strong person;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_person_0 *person; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__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_person_0 *person = __cself->person; // bound by ref

          (person->__forwarding->person) = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
          NSLog((NSString*)&__NSConstantStringImpl__var_folders_pt_w7b9v92s197g97khtskqdnn40000gn_T_main_b381a2_mi_0,(person->__forwarding->perso n)); }static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/); }static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person, 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() {
    __attribute__((__blocks__(byref))) __Block_byref_person_0 person = {(void*)0,(__Block_byref_person_0 *)&person, 33554432.sizeof(__Block_byref_person_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"))};
    void (*Block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_person_0 *)&person, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}
Copy the code

So, as usual, start with __main_block_IMPL_0

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_person_0 *person; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

No big deal, let’s look at person_0 and its initialization

struct __Block_byref_person_0 {
  void *__isa = 0; __Block_byref_person_0 *__forwarding = &personint __flags = 33554432
 int __size = sizeof(__Block_byref_person_0), 
 void (*__Block_byref_id_object_copy)(void*, void*) = __Block_byref_id_object_copy_131,
 void (*__Block_byref_id_object_dispose)(void*) = __Block_byref_id_object_dispose_131,
 Person *__strong person;
};
Copy the code

Flags = 33554432 BLOCK_HAS_COPY_DISPOSE = (1 << 25) Compiler contains a copy_Dispose assistant [that has copy and dispose functions] umm…. Then there are two functions: __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

You call _Block_object_assign and _Block_object_release.

struct __Block_byref_person_0 {
  void *__isa; / / 8 bytes
__Block_byref_person_0 *__forwarding; / / 8 bytes
 int __flags; / / 4 bytes
 int __size;  / / 4 bytes
 void (*__Block_byref_id_object_copy)(void*, void*);  / / 8 bytes
 void (*__Block_byref_id_object_dispose)(void*); / / 8 bytes
 Person *__strong person;
};
Copy the code

The address of impson_0 is 40 bytes away from the address of the person, so is +40 to find the person pointer and call through the person pointer? B: well… Some truth

__block is a local variable that belongs to the same address inside and outside the block and can be modified by code inside the block.

Block memory management

So let’s just review what we’ve learned before: how do variables/objects manage memory

Intervention: programmer manual management, the essence of the system or management of memory allocation and release

  • Automatic local basic type variables, because the value is passed, memory is followed by Block, no intervention
  • Static local basic type variable, pointer passed, because allocation in the static area, so no intervention
  • Global variables, stored in the data to go, without intervention
  • Local object variables, if on the stack, do not intervene. But when the Block is copied to the heap, it’s retained, and when the Block object is destroyed, it’s released

I find it odd to involve reference counting

Several questions why 1……. Don’t ARC and MRC call assign when they intercept __block….. idon’tknow

One more questionIf you add __block, it becomes 2, and if you add __block, it becomes 1, because in MRC environments assign will not be called.

Circular reference problems

  1. When do circular references occur and how can they be resolved?

== A circular reference occurs when a block is strongly referenced in an object and the object is used in the block. The solution is to modify the object with an __weak or __block modifier before using it in a block. 2. Add block and weak and strong before variables

  • __strong is to prevent the early release of objects held by the block, and __weak and __block are to resolve circular references
  • __block can be used by either ARC or MRC and can be used to decorate objects and basic data types
  • __weak can only be used in ARC mode and can only be used to modify objects, not basic data types
  • __block objects can be reassigned within blocks; __weak cannot
  1. How does MRC resolve circular references to __block variables? ARC how to solve?
  • MRC uses __block to resolve circular references and disallows Block to retain the referenced object.
  • The ARC period __block does not disallow a Block from making a strong reference to the object it references. The solution is to leave the variable empty in the Block, because you need to operate on the Block, so you still need the __block modifier
  • __block has two functions under MRC
    1. Allows local variables to be accessed and modified within a BLock
    1. The base Block performs the retain operation on the referenced object
  • There is only one action under ARC
    1. Allows local variables to be accessed and modified within a BLock