How to modify

Analysis starts with code:

int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; Block block = ^ { age = 20; // Cannot be modified, an NSLog(@) error is reported"%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, because the function of variables are not in the same function, of course, can not go to take it for granted to modify, that in the end how to modify it, the following will be detailed.

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.

Use the static modifier to modify the value of a variable

The memory address of the age variable can be retrieved from the __main_block_func_0 function, so the age value can be changed inside the block.

__block modify

__block is used to solve the problem that the value of the auto variable cannot be modified inside a block.

Note: __block cannot modify static and global variables

Let’s make a change to the above code and run it.

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


Copy the code

The value of auto Age can be modified by __block. What causes this? We can dig into the source code to find the answer

The source code

Again from the command line

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

To generate our main.cpp file

Struct __Block_byref_age_0 {void *__isa; // isa pointer __Block_byref_age_0 *__forwarding; // the __forwarding pointer points to itself int __flags; // The default value, not considering int __size; Int age; // age variable}; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_age_0 *age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__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_age_0 *age = __cself->age; // bound by ref __main_block_impl_0 *__cself to find the __main_block_impl_0 internal age structure (age->__forwarding->age) = 20; // Find the __forwarding pointer in the age structure, and then assign 20 to the age variable by pointing to itself in __forwarding. Modifications NSLog ((nsstrings *)&__NSConstantStringImpl__var_folders_n2_0nslhwnj04qg5hyxlg2d8ych0000gn_T_main_6af310_mi_0,(age->__forwarding->age)); } // internal function, 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*/ ); Static void __main_block_dispose_0(struct __main_block_impl_0* SRC) {_Block_object_dispose((void*)src->age, 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, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10}; Block block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344)); / / / block inside a function call ((void (__block_impl *) (*)) ((__block_impl *) block) - > FuncPtr) ((__block_impl *) block); }return 0;
}
Copy the code

It can be found in the above source code

The age variable declaration that is first modified by __block is changed to a __Block_byref_age_0 structure named age, that is, with the __block modifier, the variables captured within the block are of type __Block_byref_age_0.

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

The __Block_byref_age_0 structure age is then stored in the __main_block_IMPL_0 structure and assigned to __Block_byref_age_0 *age;

Then block is called to retrieve the age from __main_block_IMPL_0. The age structure is used to retrieve the __forwarding pointer, which stores the __Block_byref_age_0 structure itself. Age (__Block_byref_age_0) gets the age(10) variable in the structure via __forwarding and changes its value.

__forwarding

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

At this point, it’s clear why __block can change the value of a variable. __block wraps a variable as an object, and then wraps age inside a structure. The 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

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

Copy the code

The source 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 NSLog((NSString *)&__NSConstantStringImpl__var_folders_n2_0nslhwnj04qg5hyxlg2d8ych0000gn_T_main_7476f0_mi_1,(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(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; __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"))}; NSLog((NSString *)&__NSConstantStringImpl__var_folders_n2_0nslhwnj04qg5hyxlg2d8ych0000gn_T_main_7476f0_mi_0,(person.__forwarding->person )); Block block = ((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); }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. These two functions are described in detail in the next section, Memory Manager.

doubt

The following code can be executed correctly

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSMutableArray *array = [NSMutableArray array];
        Block block = ^{
            [array addObject: @"20"];
            [array addObject: @"30"];
            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.