The last article iOS underlying principles summary – explore the nature of the block (a) has been introduced in the underlying nature of the block implementation and understand the capture of variables, this article continues to explore the nature of the block.

Block Captures an object variable

Block is usually used to capture object variables, so the capture of object variables is the same as the basic data type variables?

Look at the code thinking: when is an object type accessed in a block destroyed?

typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            
            block = ^{
                NSLog(@"-- -- -- -- -- - the block internal % d",person.age); }; } // The person is not released from NSLog(@)"-- -- -- -- -- -- -- --"); } // person releasereturn 0; 
}
Copy the code

After the brace completes, the person will still not be released. As mentioned in the previous article, person is an AOTU variable, and the variable in the passed block is also Person, meaning that the block has a strong reference to Person, so peroson will not destroy the block if it is not destroyed. Looking at the source code does

Move the above code to the MRC environment, where the person is freed even though the block is still in place. Because MRC blocks are in the stack space, the stack space does not strongly reference the external Person.

Int main(int argc, const char * argv[]) {@autoreleasepool {Block Block; { Person *person = [[Person alloc] init]; person.age = 10; block = ^{ NSLog(@"-- -- -- -- -- - the block internal % d",person.age); }; [person release]; } // person is freed from NSLog(@"-- -- -- -- -- -- -- --");
    }
    return 0;
}
Copy the code

After the block calls copy, the Person is not released.

block = [^{
   NSLog(@"-- -- -- -- -- - the block internal % d",person.age);
} copy];
Copy the code

As mentioned above, only one copy of blocks in stack space is required. If blocks in stack space are copied to the heap, the person will not be released. Therefore, blocks in heap space may be retained once to ensure that the person will not be destroyed. Blocks in heap space also release the objects they hold after they destroy themselves.

That is, blocks in stack space do not strongly reference objects, and blocks in heap space have the ability to hold externally called objects, that is, to strongly reference objects or remove strong references.

__weak

With __weak added, the person is destroyed after the scope completes execution.

typedef void (^Block)(void);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Block block;
        {
            Person *person = [[Person alloc] init];
            person.age = 10;
            
            __weak Person *waekPerson = person;
            block = ^{
                NSLog(@"-- -- -- -- -- - the block internal % d",waekPerson.age);
            };
        }
        NSLog(@"-- -- -- -- -- -- -- --");
    }
    return 0;
}
Copy the code

Convert the code to c++ to see the differences. -fobjc-arc-fobjc-Runtime =ios-8.0.0 -fobjc-runtime=ios-8.0.0 -fobjc-runtime=ios-8.0.0

Xcrun-sdk iphoneOS clang-arch arm64-rewrite-fobjc-arc-fobjc-Runtime =ios-8.0.0 main.m

The __weak modifier is also used in the generated __main_block_IMPL_0.

__main_block_copy_0 and __main_block_dispose_0

When the object type variables are captured in the block, we find that the __main_block_IMPL_0 description structure __main_block_desc_0 has two more parameters copy and dispose function, check the source code:

Both copy and dispose pass in the __main_block_IMPL_0 structure itself.

Copy is essentially the __main_block_copy_0 function. The __main_block_copy_0 function internally calls the _Block_object_assign function. The _Block_object_assign function is passed the address of the person object. The Person object, and 8.

Dispose is essentially the __main_block_dispose_0 function. The __main_block_dispose_0 function calls the _Block_object_dispose function. The _Block_object_dispose function takes the person object as arguments, along with 8.

_Block_object_assign function call timing and function

When a block copies, the __main_block_copy_0 function inside __main_block_DESc_0 is automatically called, and the _Block_object_assign function inside __main_block_copy_0 is called.

The _Block_object_assign function automatically makes a strong or weak reference to a Person object depending on what type of pointer person is inside the __main_block_IMPL_0 structure. The __main_block_IMPL_0 person pointer is __strong, and the reference count is +1. If the __main_block_IMPL_0 person pointer is __strong, the reference count is +1. If the person pointer in the __main_block_IMPL_0 structure is of type __weak, it is a weak reference and the reference count remains the same.

Time and effect of _Block_object_dispose function call

The __main_block_dispose_0 function in __main_block_DESc_0 is called automatically when a block is removed from the heap. The _Block_object_dispose function is called inside __main_block_dispose_0.

The _Block_object_dispose dispose dispose of the Person object, which is similar to release and disreferences the Person object, depending on the reference count of the person object.

conclusion

  1. Once a variable captured in a block is of object type, __main_block_DESc_0 in the block structure produces two arguments copy and dispose. Because it’s an object that a block wants to own, it needs to reference that object, which is memory management. As an example, when a block captures an object with an retarn function, it automatically generates copy and Dispose to manage its internal reference objects.

  2. When a block accesses the auto variable of the object type, there is no strong reference to Person inside the block if the block is on the stack. There is no strong reference to a variable inside the block structure, regardless of whether the variable is __strong or __weak.

  3. If the block is copied to the heap. Copy calls the _Block_object_assign function and executes operations based on the __strong, __weak, and unsafe_unretained parameter of the auto variable, generating a strong or weak reference

  4. If a block is removed from the heap, the dispose function calls the _Block_object_dispose function, which automatically releases the referenced auto variable.

The problem

1. When will the following code person be destroyed?

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    Person *person = [[Person alloc] init];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"% @",person);
    });
    NSLog(@"touchBegin----------End");
}
Copy the code

Print the content

A: As mentioned earlier, in an ARC environment, a block automatically copies as a method parameter to the GCD API, so the block is in heap space and accesses a Person object with a strong reference, so the copy function inside the block strongly references person. When the block is completed and needs to be destroyed, dispose is called to release the reference to the Person object. The person will be destroyed only when there is no strong pointer to it.

2. When will the following code person be destroyed?

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    Person *person = [[Person alloc] init];
    
    __weak Person *waekP = person;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"% @",waekP);
    });
    NSLog(@"touchBegin----------End");
}
Copy the code

Print the content

A: The block has an __weak reference to waekP, so the block copy function also has a weak reference to Person. When the curly braces are completed, the Person object with no strong pointer reference is freed. Therefore, block execution prints NULL.

3. Summarize with sample code.

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { Person *person = [[Person alloc] init]; __weak Person *waekP = person; Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@)"weakP ----- %@",waekP);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"person ----- %@",person);
        });
    });
    NSLog(@"touchBegin----------End");
}
Copy the code

Print the content

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { Person *person = [[Person alloc] init]; __weak Person *waekP = person; Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@)"person ----- %@",person);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"weakP ----- %@",waekP);
        });
    });
    NSLog(@"touchBegin----------End");
}
Copy the code

Print the content

Block modifies the value of a variable

This part of the analysis is based on the following code.

int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; Block block = ^ { // age = 20; // Cannot modify NSLog(@)"%d",age);
        };
        block();
    }
    return 0;
}

Copy the code

By default, blocks cannot modify external local variables. Through the previous analysis of the source code can know.

Age is declared inside main, indicating that the memory for age is inside main’s stack space, but the code inside the block is inside __main_block_func_0. Function __main_block_func_0 does not access the memory space of the age variable. The stack space of the two functions is different. The internal age of __main_block_func_0 is the internal age of the block structure. Therefore, variables inside main cannot be modified inside __main_block_func_0.

Method 1: Use the static modifier age.

The memory address of the age variable is available in __main_block_func_0, so you can change the age value inside the block.

Method 2: __block

__block is used to solve the problem that a block cannot modify the value of the auto variable, and __block cannot modify static and global variables

__block int age = 10;
Copy the code

The compiler wraps the __block modified variable into an object and looks at the underlying c++ source code.

It can be found in the above source code

The first to be__blockModification of theageThe variable declaration becomes namedagethe__Block_byref_age_0Structure, that is, plus__blockCaptured by modifying wordsblockThe variable of is__Block_byref_age_0Type of structure.

Look at the following figure to see what elements are stored inside the __Block_byref_age_0 structure.

The __isa pointer: __Block_byref_age_0 also has isa Pointers, meaning that __Block_byref_age_0 is also an object.

__forwarding: __forwarding is of the __Block_byref_age_0 structure type, and __forwarding stores the value of (__Block_byref_age_0 *)&age, which is the structure’s own memory address.

__flags :0

__size: sizeof(__Block_byref_age_0) is the memory space occupied by __Block_byref_age_0.

Age: Where variables are actually stored, local variable 10 is stored here.

then__Block_byref_age_0The structure of the bodyagedeposit__main_block_impl_0Structure, and assign the value to__Block_byref_age_0 *age;

After the callblock, first take out__main_block_impl_0In theageAnd get it through the age structure__forwardingPointers, mentioned aboveIn the __forwardingWhat is preserved is__Block_byref_age_0The structure itself, in this case, isage(__Block_byref_age_0), through the__forwardingIt goes into the structureage(10)Variable and modify its value.

You get the age value in the same way when you use age in the NSLog.

Why get the value of the age variable via __forwarding?

__forwarding is a pointer to itself. This is done to facilitate memory management, as explained in the memory management section below.

That’s it.__blockIt’s already clear why you can change the value of a variable.__blockWrap variables into objects, and then wrap them inageThe variables stored inside the block are Pointers to the structure, which can be used to find the memory address and change the value of the variable.

__block modifies the object type

What if the variable itself is an object type? Generate c++ source code to view through the following code

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block Person *person = [[Person alloc] init];
        NSLog(@"% @",person);
        Block block = ^{
            person = [[Person alloc] init];
            NSLog(@"% @",person);
        };
        block();
    }
    return 0;
}
Copy the code

Wrap the object in a new structure through the source code review. There is a person object inside the structure. The difference is that two memory management functions, __Block_byref_id_object_copy and __Block_byref_id_object_dispose, are added inside the structure

__Block_byref_id_object_copyand__Block_byref_id_object_disposeThe timing of function calls and functions are analyzed in detail in the __block memory management section.

The problem

1. Check whether the following codes can be executed correctly

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray *array = [NSMutableArray array];
        Block block = ^{
            [array addObject: @"5"];
            [array addObject: @"5"];
            NSLog(@"% @",array);
        };
        block();
    }
    return 0;
}
Copy the code

A: This works because only the array memory address is used in the block block. Adding to the memory address does not change the arry memory address, so array does not need to use __block decorations to compile correctly.

Therefore, when using only the memory address of the local variable, rather than modifying it, try not to add __block. From the above analysis, we know that once adding __block modifier, the system will automatically create the corresponding structure, occupying unnecessary memory space.

2. Mentioned above__blockModification of theageVariables are encapsulated as structures at compile time, so when used externallyageFor variables, use__Block_byref_age_0What about the structure? or__Block_byref_age_0What about the age variable in the structure?

To verify the above problem, we also use the custom structure to view its internal structure

typedef void (^Block)(void);

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

struct __main_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(void);
    void (*dispose)(void);
};

struct __Block_byref_age_0 {
    void *__isa;
    struct __Block_byref_age_0 *__forwarding;
    int __flags;
    int __size;
    int age;
};
struct __main_block_impl_0 {
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    struct __Block_byref_age_0 *age; // by ref
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int age = 10;
        Block block = ^{
            age = 20;
            NSLog(@"age is %d",age);
        };
        block();
        struct __main_block_impl_0 *blockImpl = (__bridge struct __main_block_impl_0 *)block;
        NSLog(@"%p",&age);
    }
    return 0;
}

Copy the code

Print breakpoints to view the internal structure of the structure

Find the age structure by looking at the contents of the blockImpl structure, which focuses on two elements:

  1. __forwardingThe address stored there is indeed the address of the age structure variable itself
  2. ageStore the modified variable 20 in.

As mentioned above, age is always found in a block by the __Block_byref_age_0 structure.

In order to hide the implementation of the __Block_byref_age_0 structure, Apple printed the address of the age variable and found that it was actually the address of the age variable inside the __Block_byref_age_0 structure.

Print can be found through the calculation of the figure aboveageThe address of the same__Block_byref_age_0Structure of the bodyageValues have the same address. So the age that we use outside is the age that we use inside the structure. So it’s just for useageThat’s what we said beforeint age.

__block memory management

As mentioned above, when variables of object type are captured in the block, copy and Dispose function are automatically added to the __main_block_DESc_0 structure to manage the captured variables in memory.

So similarly, when a block inside captures a variable of an object type modified by __block, The __Block_byref_person_0 structure also automatically adds __Block_byref_id_object_copy and __Block_byref_id_object_dispose to manage the memory of objects wrapped as a structure by __block.

When blocks are on the stack, there is no memory management for __block variables. When blcok is copied to the heap, the block’s internal copy function is called, which calls the _Block_object_assign function. The _Block_object_assign function forms a strong reference (equivalent to retain) to the __block variable.

Let’s start by looking at the memory changes as blocks are copied onto the heap

When a block is copied to the heap, the __block variable referenced inside the block is copied to the heap and holds the variable. If the block is copied to the heap while the __block variable is already on the heap, it is not copied.

When a block is removed from the heap, the dispose function, __main_block_dispose_0, is called. The _Block_object_dispose function is called inside the __main_block_dispose_0 function, The referenced __block variable is automatically released.

The block internally decides when to copy variables to the heap and when to do reference counting on variables.

Variables modified by __block are always strongly referenced in the block structure, whereas other types are determined by the object pointer type passed in.

A piece of code takes a closer look.

typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int number = 20;
        __block int age = 10;
        
        NSObject *object = [[NSObject alloc] init];
        __weak NSObject *weakObj = object;
        
        Person *p = [[Person alloc] init];
        __block Person *person = p;
        __block __weak Person *weakPerson = p;
        
        Block block = ^ {
            NSLog(@"%d",number); // local variable NSLog(@"%d",age); // The __block modified local variable NSLog(@"%p",object); // The local variable NSLog(@) of the object type"%p",weakObj); // The __weak modified local variable NSLog(@) of the object type"%p",person); // __block modifies the local variable NSLog(@) of the object type"%p",weakPerson); // __block, __weak modified local variable of object type}; block(); }return 0;
}
Copy the code

Translate the above code into c++ code to see the differences between different variables

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int number; NSObject *__strong object; NSObject *__weak weakObj; __Block_byref_age_0 *age; // by ref __Block_byref_person_1 *person; // by ref __Block_byref_weakPerson_2 *weakPerson; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, NSObject *__strong _object, NSObject *__weak _weakObj, __Block_byref_age_0 *_age, __Block_byref_person_1 *_person, __Block_byref_weakPerson_2 *_weakPerson, int flags=0) : number(_number), object(_object), weakObj(_weakObj), age(_age->__forwarding), person(_person->__forwarding), weakPerson(_weakPerson->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

As shown in the __main_block_IMPL_0 structure above, variables (object and weadObj) that are not modified with __block are strongly or weakly referenced according to their own pointer types captured by the block. Once a variable modified with __block is used, The __main_block_IMPL_0 structure uses strong Pointers to reference the generated structure.

What is the difference between the structures generated by __block modified variables

struct __Block_byref_age_0 {
  void *__isa;
__Block_byref_age_0 *__forwarding;
 int __flags;
 int __size;
 int age;
};

struct __Block_byref_person_1 {
  void *__isa;
__Block_byref_person_1 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__strong person;
};

struct __Block_byref_weakPerson_2 {
  void *__isa;
__Block_byref_weakPerson_2 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__weak weakPerson;
};
Copy the code

As analyzed above, the structure generated by __block modifies the variables of the object type has two functions, __Block_byref_id_object_copy and __Block_byref_id_object_dispose, which are used to perform memory management operations on the variables of the object type. The type of reference a structure makes to an object depends on the variable of the object type that the block captures. WeakPerson is a weak pointer, so __Block_byref_weakPerson_2 is a weak reference to weakPerson, and Person is a strong pointer, so __Block_byref_person_1 is a strong reference to person.

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
    _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_assign((void*)&dst->object, (void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
    _Block_object_assign((void*)&dst->weakObj, (void*)src->weakObj, 3/*BLOCK_FIELD_IS_OBJECT*/);
    _Block_object_assign((void*)&dst->person, (void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);
}
Copy the code

The __main_block_copy_0 function treats variables differently depending on whether they are strong or weak Pointers and whether they are modified by __block. Strong Pointers generate strong references within blocks, and weak Pointers generate weak references within blocks. The last argument to a variable modified by __block is passed 8, and the last argument to a variable not modified by __block is passed 3.

Dispose of blocks as they are removed from the heap using the dispose function.

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
    _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_dispose((void*)src->object, 3/*BLOCK_FIELD_IS_OBJECT*/);
    _Block_object_dispose((void*)src->weakObj, 3/*BLOCK_FIELD_IS_OBJECT*/);
    _Block_object_dispose((void*)src->person, 8/*BLOCK_FIELD_IS_BYREF*/);
    _Block_object_dispose((void*)src->weakPerson, 8/*BLOCK_FIELD_IS_BYREF*/);
    
}
Copy the code

__forwarding pointer

As mentioned above, the __forwarding pointer points to the structure itself. When using variables, the __forwarding pointer is found through the structure, and the corresponding variable is found through the __forwarding pointer. This is designed to facilitate memory management. From the memory management analysis of __block variables above, we know that when a block is copied to the heap, variables referenced in the block are also copied to the heap.

Let’s go back to the source code. When modifying a variable modified by __block in a block.

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_age_0 *age = __cself->age; // bound by ref
            (age->__forwarding->age) = 20;
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_jm_dztwxsdn7bvbz__xj2vlp8980000gn_T_main_b05610_mi_0,(age->__forwarding->age));
        }
Copy the code

In this case, __Block_byref_age_0. Find the __forwarding pointer. The __forwarding pointer points to the structure itself, so you can find the age variable to modify.

When a block is in the stack, the __forwarding pointer inside the __Block_byref_age_0 structure points to the structure itself.

When a block is copied to the heap, the stack’s __Block_byref_age_0 structure is also copied to the heap. The __forwarding pointer in the stack’s __Block_byref_age_0 structure points to the heap’s __Block_byref_age_0 structure. The __forwarding pointer in the heap’s __Block_byref_age_0 structure still points to itself.

In this case, when the age is modified

__Block_byref_age_0 *age = __cself->age; // bound by ref // age->__forwarding->age = 20;Copy the code

The changed variable is subtly assigned to __Block_byref_age_0 in the heap via the __forwarding pointer.

We use a diagram to show the __forwarding pointer in action

So the variables that you get inside the block are actually on the heap. The _Block_object_assign function that performs these operations when a block is copied to the heap.

Memory management of object types modified by __block

Using the following code, generate c++ code to see the internal implementation

typedef void (^Block)(void);
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block Person *person = [[Person alloc] init];
        Block block = ^ {
            NSLog(@"%p", person);
        };
        block();
    }
    return 0;
}
Copy the code

Go to the source to see the __Block_byref_person_0 structure and its declaration

The __Block_byref_person_0 structure typedef void (*Block)(void); struct __Block_byref_person_0 { void *__isa; // 8 memory space __Block_byref_person_0 *__forwarding; // 8 int __flags; // 4 int __size; // 4 void (*__Block_byref_id_object_copy)(void*, void*); // 8 void (*__Block_byref_id_object_dispose)(void*); // 8 Person *__strong person; / / 8}; // 8 + 8 + 4 + 4 + 8 + 8 + 8 = 48Copy the code
// the __Block_byref_person_0 structure declares __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"))};Copy the code

Void (*__Block_byref_id_object_copy)(void*, void*); And void __Block_byref_id_object_dispose (*) (void *); . These two functions provide memory management operations for __block decorated objects.

Void (*__Block_byref_id_object_copy)(void*, void*); And void __Block_byref_id_object_dispose (*) (void *); The assigned values are __Block_byref_id_object_copy_131 and __Block_byref_id_object_dispose_131. Find these two functions

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_byref_id_object_copy_131 also calls the _Block_object_assign function. The _Block_object_assign function internally gets the DST pointer, which is the block object’s own address value plus 40 bytes. And the last parameter that _Block_object_assign passes in is 131, which is different from the 3,8 parameters that block passes in to directly manage the memory of the object. You can guess that _Block_object_assign does different operations internally depending on the parameters passed in.

The __Block_byref_person_0 structure occupies 48 bytes by calculating the space occupied by the structure above. And adding 40 just happens to point to the Person pointer.

That is, copy passes the Person address to the _Block_object_assign function, which makes a strong or weak reference to the Person object.

Take a look at the source if you use the __weak modifier

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        __block __weak Person *weakPerson = person;
        Block block = ^ {
            NSLog(@"%p", weakPerson);
        };
        block();
    }
    return 0;
}
Copy the code
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_weakPerson_0 *weakPerson; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

There is no change in __main_block_IMPL_0, __main_block_IMPL_0 is still a strong reference to weakPerson, but __Block_byref_weakPerson_0 is a __weak pointer to weakPerson.

struct __Block_byref_weakPerson_0 {
  void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 Person *__weak weakPerson;
};
Copy the code

That is, any structure generated within a block that modifies a __block variable is a strong reference, and a reference to an external variable within a block depends on whether the variable passed inside the block is a strong or weak reference.

Under MRC, the __block structure does not make a strong reference to Person and remains a weak reference, despite the call to copy.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block Person *person = [[Person alloc] init];
        Block block = [^ {
            NSLog(@"%p", person);
        } copy];
        [person release];
        block();
        [block release];
    }
    return 0;
}
Copy the code

The above code person will be released first

Block copy[50480:8737001] -[Person dealloc] Block copy[50480:8737001] 0x100669a50Copy the code

When a block is removed from the heap. Dispose function is called to remove the __Block_byref_person_0 *person from the block block. The dispose operation is also called in the __Block_byref_person_0 structure to remove the Person * Person; The reference. To ensure that the structure and the objects inside the structure can be released normally.

A circular reference

Circular references cause memory leaks.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        person.age = 10;
        person.block = ^{
            NSLog(@"%d",person.age);
        };
    }
    NSLog(@"Curly braces closed.");
    return 0;
}
Copy the code

Run the code to print the content

Block copy[55423:9158212] curly braces endCopy the code

You can see that after the braces close, the person is still not released, resulting in a circular reference.

Take a look at the memory structure between them

As you can see in the figure above, the Person object and the block object have strong references to each other, so that neither can be freed, resulting in a memory leak.

Solve the circular reference problem – ARC

First, in order to be able to execute a block at any time, we definitely want a strong reference from Person to block, and weak references from person inside a block are best.

The __weak and __unsafe_unretained references can be used to solve this problem

We also mentioned above that __weak causes Pointers to be weak inside blocks. If a block is a weak pointer to a Person object, there will be no cross-references that will prevent it from being freed.

__weak__unsafe_unretainedThe difference between.

__weak does not produce strong references, and the pointer is automatically set to nil when the object to which it points is destroyed. Therefore, the problem is usually solved by __weak.

__unsafe_unretained does not generate a forward reference, which is unsafe. When the referenced object is destroyed, the address stored in the pointer remains unchanged.

Using __block can also solve the problem of circular references.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block Person *person = [[Person alloc] init];
        person.age = 10;
        person.block = ^{
            NSLog(@"%d",person.age);
            person = nil;
        };
        person.block();
    }
    NSLog(@"Curly braces closed.");
    return 0;
}
Copy the code

Cross-references between the above code can be shown in the following figure

As we mentioned above, using a variable inside a block is actually using the person object inside the __Block_byref_person_0 structure generated by the __block variable, so when the person object is set to nil it breaks the structure’s strong reference to Person, Then the circular reference to the triangle is automatically broken. It will be released when it’s time to be released. The downside, however, is that you have to execute the block and set the Person object to nil inside the block. This means that the code leaks because of a circular reference before the block executes.

Solve circular reference problem – MRC

Use __unsafe_unretained. __weak is not supported in MRC. The principle of using __weak is the same as that of using ARC.

Using __block also solves the problem of circular references. As mentioned in the __block memory management section above, the __block structure does not make a strong reference to Person in MRC environments, even though copy is called. So it can also solve the problem of circular reference.

__strong__weak

__weak typeof(self) weakSelf = self;
person.block = ^{
    __strong typeof(weakSelf) myself = weakSelf;
    NSLog(@"age is %d", myself->_age);
};
Copy the code

Reusing the __strong modifier self variable inside the block is to have a strong pointer to weakSelf inside the block so that weakSelf is not destroyed by the time the block is called.

The interview questions

The interview questions mentioned above can be found in both articles. I won’t repeat it here.

Underlying principles article column

Underlying principles article column


Welcome to point out any mistakes in the article. I am XX_CC, a long grown but not enough of a guy.