The nature of the block

1. Basic usage of block

// block void (^block)(void) = ^{NSLog(@"Hello, World!" ); }; block(); Void (^block)(int, int) = ^(int a, int b) NSLog(@"this is a block!" ); }; block(10, 20);Copy the code

2. A __main_block_impl_0 structure is generated after converting block code to C++ file. Block is a pointer to this structure

int age = 20; void (^block)(int, int) = ^(int a , int b){ NSLog(@"this is a block! -- %d", age); NSLog(@"this is a block!" ); NSLog(@"this is a block!" ); NSLog(@"this is a block!" ); }; block(10, 10); Int main(int argc, const char * argv[]) {/* @autoreleasepool */ {__AtAutoreleasePool __autoreleasepool; int age = 10; // Block = &__main_block_impl_0 (__main_block_func_0, &__main_block_desc_0_data, age) void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age)); __main_block_impl_0 can be converted directly to __block_impl because the struct addresses of the two types are the same. // (block->FuncPtr)(block, 10, 10) ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10); } return 0; }Copy the code

3. __main_block_IMPL_0 struct, which contains __block_IMPl and __main_block_desc_0 struct variable Desc, A constructor that returns a value of type __main_block_IMPL_0 and generates an age to store the values referenced outside

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; // the address of __main_block_func_0 is passed to fp: Age (_age) syntax automatically assigns _ag to age __main_block_IMPL_0 (void *fp, struct __main_block_desc_0 * DESC, int _age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; // the _NSConcreteStackBlock that isa points to is the current block type impl.Flags = Flags; impl.FuncPtr = fp; // Save the address of __main_block_func_0, which is the logical function of block Desc = Desc; __main_block_desc_0_DATA (__main_block_impl_0) returns the __main_block_impl_0 structure}};Copy the code

4. The __block_impl structure contains an ISA pointer, indicating that block is also an OC object. In the __main_block_IMPL_0 constructor isa points to an address of type _NSConcreteStackBlock, which is also the real type of the block at compile time

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

5.__main_block_func_0 is a function that encapsulates the logic code of block execution. The address of the execution code is stored by assigning void *fp to the FuncPtr pointer in the __main_block_impl_0 constructor

Static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) { int age = __cself->age; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_1); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_2); NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_3); }Copy the code

6. The reserved value of __main_block_desc_0_DATA is 0, and the Block_size value is sizeof(struct __main_block_IMPL_0). This is the current size of the __main_block_IMPL_0 structure. Assigns to desc by parameter desc in the __main_block_IMPL_0 constructor

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

The age(_age) syntax automatically assigns _age to the variable int age to save. Moreover, the converted __main_block_IMPL_0 call can be directly converted to __block_impl because the struct addresses of the two types are the same and the value of __block_IMPl is put directly into __main_block_IMPL_0

conclusion
  • blockIt’s essentially oneOC objectIt also has an insideIsa pointer
  • blockEncapsulates the function call and the function call environmentOC object

The essential structure of a block can be summarized in the following diagram

Block variable capture

To ensure that external variables can be accessed within a block, blocks have a variable capture mechanism

  • Local variables default toautoModified to indicate automatic variables that are destroyed when out of scope.blockThe capture variable isValue passed
  • Local variables arestaticModifier, which will remain in memory and will not be freed, block’s capture of the variable isPointer passed
  • Global variables do not need to be captured because they are always in memory

The type of the block

  • blockThere are 3 types that can be calledclassMethods orIsa pointerLook at the specific type, which is ultimately inherited fromNSBlocktype
    • __NSGlobalBlock__ (_NSConcreteGlobalBlock)
    • __NSStackBlock__ (_NSConcreteStackBlock)
    • __NSMallocBlock__ (_NSConcreteMallocBlock)
  • blockThe real types of are run time, passClangCompile theC++The type is not the most accurate because some changes and processing are made at run time. And now,LLVMOnly an intermediate file is generated, andClangGenerated files are different

Observe the corresponding output of the block in the following code

int main(int argc, const char * argv[]) { @autoreleasepool { void (^block)(void) = ^{ NSLog(@"Hello"); }; NSLog(@"%@", [block class]); NSLog(@"%@", [[block class] superclass]); NSLog(@"%@", [[[block class] superclass] superclass]); } return 0; } // Corresponding output: __NSGlobalBlock__ NSBlock NSObjectCopy the code

Different memory regions correspond to different block types

  • The data segment corresponds to__NSGlobalBlock__The type of block,
  • The heap segment corresponds to__NSMallocBlock__The type of block,
  • The stack segment corresponds to__NSStackBlock__The type of block,

Different types of blocks correspond to different operations

  • The type of block that does not access automatic variables is__NSGlobalBlock__
  • The type of block that accesses automatic variables is__NSStackBlock__
  • __NSStackBlock__Block that calls copy will change to__NSMallocBlock__

Each type of block calls copy as shown below

Set Xcode Build Setting-> Objective-c Automatic Reference Counting to No, set Xcode Build Setting-> Objective-c Automatic Reference Counting to No, set Xcode Build Setting-> Objective-c Automatic Reference Counting to No

int main(int argc, const char * argv[]) { @autoreleasepool { int a = 10; Void (^block1)(void) = ^{NSLog(@"Hello"); }; int age = 10; void (^block2)(void) = ^{ NSLog(@"Hello - %d", age); }; NSLog(@"%@ %@ %@", [block1 class], [[block2 copy] class], [^{ NSLog(@"%d", age); } class]); } return 0; } // The corresponding output: __NSGlobalBlock__ __NSMallocBlock__ __NSStackBlock__Copy the code

Note: Block2 is of type in the MRC environment__NSStackBlock__Is stored in the stack segment. It’s only changed by copy modifier__NSMallocBlock__, stored in the heap. This is true even without the use of copy in the ARC environment__NSMallocBlock__Because the compiler automatically copies as needed

Block copy operation

In an ARC environment, the compiler automatically copies blocks on the stack to the heap as appropriate

1. When block is returned by a function

If you do not copy, the return value of the block inside myBlock is released as soon as the scope of the block ends

typedef void (^Block)(void);

Block myblock()
{
    int age = 10;
    return ^{
        NSLog(@"---------%d", age);
    };
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       Block block = myblock();
        block();
        NSLog(@"%@", [block class]); // __NSMallocBlock__
    }
    return 0;
}
Copy the code

2. Assign block to the __strong pointer

typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; / / strong pointer Block, Block, Block, Block = ^ {NSLog (@ "-- -- -- -- -- -- -- -- - % d", the age); }; NSLog(@"%@", [block class]); // __NSMallocBlock__ } return 0; }Copy the code

3. Block is used as a Cocoa API method name containing the usingBlock method parameter

NSArray *array = @[];
[array enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
          
}];
Copy the code

4. Block as a method parameter of the GCD API

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
  
});
   
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  
});
Copy the code

How to write block properties in different environments

1. The recommended writing method of the block attribute under MRC

@property (copy, nonatomic) void (^block)(void);
Copy the code

2. Recommended writing of the block attribute in ARC

Void (^block)(void); void (^block)(void); @property (copy, nonatomic) void (^block)(void);Copy the code

The auto variable of object type

When the block internally accesses the auto variable of the object type

1. If the block is on the stack, there is no strong reference to the auto variable

@interface Person : NSObject @property (assign, nonatomic) int age; @end @implementation Person - (void)dealloc { [super dealloc]; NSLog(@"Person - dealloc"); } @end typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { Block block; { Person *person = [[Person alloc] init]; person.age = 10; block = ^{ NSLog(@"---------%d", person.age); }; // MRC environment corresponding memory management [person release]; NSLog(@"------%@", [block class]); } // In the MRC environment, since blocks are in the stack, age is not strongly referenced, and person will release NSLog when scope ends (@"------"); } return 0; }Copy the code

2. If a block is copied to the heap, operations are performed based on the __strong, __weak, and __unsafe_unretained parameter of the auto variable

@interface Person : NSObject @property (assign, nonatomic) int age; @end @implementation Person - (void)dealloc { NSLog(@"Person - dealloc"); } @end typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { Block block; { Person *person = [[Person alloc] init]; person.age = 10; // __strong Person *weakPerson = person; __weak Person *weakPerson = person; block = ^{ NSLog(@"---------%d", weakPerson); }; NSLog(@"------%@", [block class]); } // In the ARC environment, blocks are automatically copied to the heap, the __strong and __weak modifiers are toggled, and the NSLog(@"------") is freed by person; } return 0; }Copy the code

Copy is called from the block’s __main_block_desc_0 structure, and _Block_object_assign is called from the copy function. The _Block_object_assign function performs operations based on the __strong, __weak, and __unsafe_unretained modifier of the auto variable, generating a retained or weak reference

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

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};

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, 3/*BLOCK_FIELD_IS_OBJECT*/);}
Copy the code

3. If a block is removed from the heap, its internal dispose function is called, which calls the _Block_object_dispose function. The _Block_object_dispose function automatically releases the referenced auto variable (release).

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

4. Block does not generate copy and dispose functions only when it references basic data types

5. If you modify the object type with static, the generated C++ code is as follows

Block block; { static NSString *string = @"haha"; block = ^{ NSLog(@"---------%@", string); }; } // generate C++ code struct __main_block_impl_0 {struct __block_impl impl; struct __main_block_desc_0* Desc; // The type of the string variable is NSString ** NSString *__strong *string; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSString *__strong *_string, int flags=0) : string(_string) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }};Copy the code

Note: there is in the code__weakConverting C++ files may cause an errorcannot create __weak reference in file using manual reference, you can specify support for ARC and runtime system versions

Xcrun-sdk iphoneOS clang-arch arm64-rewrite-fobjc-arc-fobjc-Runtime =ios-8.0.0 source fileCopy the code

__block qualifier

__block decorates basic data types

How can I change the age value inside a block

typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { int age = 10; Block block1 = ^{ // age = 20; NSLog(@"age is %d", age); }; block1(); NSLog(@"age memory address - %p", &age); } return 0; }Copy the code

1. The age property is static. Inside the block, the address value of the age property can be changed according to the address. The downside is that the age property will remain in memory, causing excess memory usage and changing the nature of the age property so that it is no longer an auto variable

static int age = 10;
Copy the code

2. Use __block to modify attributes. The underlying structure object of type __Block_byref_age_0 contains the real value of age

__block int age = 10;
Copy the code

__main_block_IMPL_0 (age); the address of the age attribute is also the address of the age variable in the __Block_byref_age_0 structure. The goal is that you don’t need to know the actual implementation inside, and all you see is the printed value

struct __Block_byref_age_0 { void *__isa; __Block_byref_age_0 *__forwarding; // save your own address int __flags; int __size; int age; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_age_0 *age; // by ref __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; }}; int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; // __Block_byref_age_0 age = {0, &age, 0, sizeof(__Block_byref_age_0), 10}; __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10}; Block block1 = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344)); ((void (*)(__block_impl *))((__block_impl *)block1)->FuncPtr)((__block_impl *)block1); } return 0; }Copy the code
Conclusion:
  • __blockCan be used to solveblockInternal cannot be modifiedautoVariable value problem
  • The compiler will take__blockA variable is wrapped as an object
  • Actually, the variable I’m changing is__blockThe value of the variable stored inside the generated object, not outsideAuto variable, but internally generates the same variable address as the outsideAuto variableThe address values are the same, so any variable that changes the inside will also change the outsideAuto variable
  • __blockCan’t modify global variables, static variables
Memory management of __block

1. When the program is compiled, both block and __block are on the stack, and no strong references to __block variables are generated

2. As __block is also wrapped as an OC object, copy and dispose functions are also generated at the bottom of the block

3. When a block is copied to the heap, the block’s internal copy function is called. The internal copy function calls the _Block_object_assign function, which forms a strong reference to the __block variable.

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}; 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*/); }Copy the code

In fact, the __block variable is copied to the heap because it is wrapped as an OC object. If a block strongly references __block, the __block variable is already copied to the heap, so it will not be copied again

3. When a block is removed from the heap, its internal dispose function is called, which calls the _Block_object_dispose function. The _Block_object_dispose function automatically releases the referenced __block variable (release)

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); }Copy the code

If more than one block holds a __block variable, the __block variable will not be released until all blocks are removed from the heap

The difference between __block and OC objects in blocks

Look at the following code, what is the essential difference in a block

__block int age = 10;
NSObject *obj = [[NSObject alloc] init];
        
Block block1 = ^{
  age = 20;
  
  NSLog(@"age is %d", age);
  NSLog(@"obj is %p", obj);
};
Copy the code

A C++ file finds that __block generates strong references, and NSObject objects use the __strong or __weak modifiers to distinguish retain from weak

struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; NSObject *__strong obj; __Block_byref_age_0 *age; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__strong _obj, __Block_byref_age_0 *_age, int flags=0) : obj(_obj), age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; 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->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/); }Copy the code

Note:__weakCannot modify basic data types, the compiler reports__weak' only applies to Objective-C object or block pointer types; type here is 'int'warning

__forwarding pointer
  • In the stack,__blockIn the__forwarding pointerPoints to its own memory address
  • Once it’s copied to the heap,__forwarding pointerPoint to the heap__block
  • In the heap__forwardingPoint to the heap__block
  • Such purposes are all for whatever visit is made__blockWhether it’s on the stack or on the heap, it can pass__forwarding pointerFind the storage in the heapAuto variable

__block modifies the object type

1. Look at the following code. When is an object type decorated with __block freed

typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { Block block; { Person *person = [[Person alloc] init]; person.age = 10; __block Person *weakPerson = person; block = ^{ NSLog(@"---------%d", weakPerson.age); }; NSLog(@"------%@", [block class]); } // Here is the break point to see if the person is released NSLog(@"------"); } return 0; }Copy the code

2. Convert to C++ file, you can find that __block structure generated at the bottom will reference the object type, and the default is __strong to modify, and the corresponding internal generation of copy and dispose function

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 *__strong weakPerson;
};
Copy the code

3. In the main function, __Block_byref_id_object_copy_131 and __Block_byref_id_object_dispose_131 will be assigned to the __Block_byref_weakPerson_0 structure object

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        Block block;

        {
            Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
            ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)person, sel_registerName("setAge:"), 10);

            // __Block_byref_weakPerson_0 weakPerson = {0, &weakPerson, 33554432, sizeof(__Block_byref_weakPerson_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, person};
            __attribute__((__blocks__(byref))) __Block_byref_weakPerson_0 weakPerson = {(void*)0,(__Block_byref_weakPerson_0 *)&weakPerson, 33554432, sizeof(__Block_byref_weakPerson_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, person};

            block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_weakPerson_0 *)&weakPerson, 570425344));


            NSLog((NSString *)&__NSConstantStringImpl__var_folders_wn_kcs2c07n3mqd02d77tvjgtjr0000gn_T_main_8be7f8_mi_1, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)block, sel_registerName("class")));
        }

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_wn_kcs2c07n3mqd02d77tvjgtjr0000gn_T_main_8be7f8_mi_2);
    }
    return 0;
}
Copy the code

4. Find these two values can be found that the two functions of _Block_object_assign and _Block_object_dispose will be called respectively, and the passed object is __Block_byref_weakPerson_0 weakPerson object, That is to say, the structure also retains and releases the weakPerson object

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

5. We add the __weak modifier to weakPerson in the first code, and then run the program. When the scope ends, the Person object will also be released

typedef void (^Block)(void); int main(int argc, const char * argv[]) { @autoreleasepool { Block block; { Person *person = [[Person alloc] init]; person.age = 10; __block __weak Person *weakPerson = person; block = ^{ NSLog(@"---------%d", weakPerson.age); }; NSLog(@"------%@", [block class]); } // Here is the break point to see if the person is released NSLog(@"------"); } return 0; }Copy the code

6. If we convert to C++ file, we can find that the person object modifier in __Block_byref_weakPerson_0 becomes __weak

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
Conclusion:
  • __blockThe modified object type also generates a new structure object, and is only modified byblockStrong reference, same as__blockThe modifier base data types are the same
  • __blockMember variables of the object type are also generated internally, depending on the modifier__strongand__weakCorresponds to whether the object type is strongly referenced
  • __blockThe inside will also be generatedcopyanddisposefunction
    • when__blockVariables arecopyTo the heap

, which calls the copy function inside the __block variable. Copy calls the _Block_object_assign function inside, The _Block_object_assign function relies on the modifier (__strong, __weak, __unsafe_unretained) of the object it points to, resulting in a retained or weak reference – if the __block variable is removed from the heap, The dispose function inside the __block variable is called, which calls the _Block_object_dispose function. The _Block_object_dispose function automatically releases the pointed object (release).

Note: In MRC, the __block modifier is used, and only a weak reference is made to the auto variable. The __block modifier is released before the block is released, regardless of whether __weak is added, unlike in ARC

A circular reference

Blocks can easily cause circular reference problems in use, as shown in the following code

typedef void (^Block) (void); @interface Person : NSObject @property (copy, nonatomic) Block block; @property (assign, nonatomic) int age; - (void)test; @end@implementation Person - (void)test {// inner loop reference self.block = ^{NSLog(@"age is %d", self.age); }; } @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [[Person alloc] init]; person.age = 10; Block = ^{NSLog(@"age is %d", person.age); }; } NSLog(@"111111111111"); return 0; }Copy the code

The block attribute in the Person object strongly refers to the block object, and the block object also has a Person member variable that points to the Person object, creating a circular reference that no one can release

The solution

In the ARC environment

Make one of the Pointers a weak reference

1. If __weak is used, strong references are not generated and Pointers are automatically set to nil when the object they point to is destroyed

@implementation Person

- (void)test {
	__weak typeof(self) weakSelf = self;
   
    self.block = ^{
        NSLog(@"age is %d", weakSelf.age);
    };
}
@end

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

2. __unsafe_unretained does not generate strong references, but it is unsafe. When the pointed object is destroyed, the address stored in the pointer remains unchanged, but it still points to the reclaimed memory space

@implementation Person

- (void)test {
	__unsafe_unretained typeof(self) weakSelf = self;
   
    self.block = ^{
        NSLog(@"age is %d", weakSelf.age);
    };
}
@end

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

3. Use __block to solve the problem. Modifying an object with __block will cause three references to each other and cause circular references

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

Inside the block you also need to manually empty person, which is a variable generated inside __block to point to the Person object

In the MRC environment

1. Use __unsafe_unretained. The same as in ARC, but MRC does not support __weak

Person *person = [[Person alloc] init];
__unsafe_unretained typeof(person) weakPerson = person;

person.block = [^{
  NSLog(@"age is %d", weakPerson.age);
} copy];

[person release];
Copy the code

In MRC, the __block object is not a strong reference to the Person object, so it does not cause a circular reference

__block Person *person = [[Person alloc] init];        
person.age = 10;

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

The interview questions

1. Look at the following code, what are the values entered respectively

int a = 10; static int b = 10; int main(int argc, const char * argv[]) { @autoreleasepool { auto int age = 10; static int height = 10; void (^block)(void) = ^{ NSLog(@"age is %d, height is %d", age, height); NSLog(@"a is %d, b is %d", a, b); }; age = 20; height = 20; a = 20; b = 20; block(); Age =10, height=20, a=20, b=20} return 0; }Copy the code

Age is an automatic variable and is value passing

Height represents pointer passing, and block captures the address of the variable

Both a and B are global variables, so blocks don’t need to capture them at all

int a = 10; static int b = 10; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int age; int *height; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int *_height, int flags=0) : age(_age), height(_height) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; auto int age = 10; static int height = 10; void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age, &height)); age = 20; height = 20; a = 20; b = 20; ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); } return 0; }Copy the code

2. Look at the following code to see if self is caught inside the block

@interface Person : NSObject

@property (copy, nonatomic) NSString *name;

- (instancetype)initWithName:(NSString *)name;
@end

@implementation Person

- (void)test
{
    void (^block)(void) = ^{
        NSLog(@"-------%d", [self name]);
    };
    block();
}
 
- (instancetype)initWithName:(NSString *)name
{
    if (self = [super init]) {
        self.name = name;
    }
    return self;
}

@end
Copy the code

Will be captured. Because self is also a local variable by nature, a variable is generated inside the block to hold the address of the Person object

struct __Person__test_block_impl_0 { struct __block_impl impl; struct __Person__test_block_desc_0* Desc; Person *self; __Person__test_block_impl_0(void *fp, struct __Person__test_block_desc_0 *desc, Person *_self, int flags=0) : self(_self) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; // All functions generate implicit arguments self and _cmd static void _I_Person_test(Person * self, SEL _cmd) { void (*block)(void) = ((void (*)())&__Person__test_block_impl_0((void *)__Person__test_block_func_0, &__Person__test_block_desc_0_DATA, self, 570425344)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); }Copy the code

3. What does __block do? What is the use of attention

You can wrap the decorated variable as an object, solving the problem of not being able to modify external variables inside a block.

__block has internal memory management and no strong references to objects in MRC environments

4. Why is copy the attribute modifier of block? What are the implications of using blocks?

Once a block is not copied, it is not on the heap. The purpose of putting it on the heap is to allow us to control its life cycle so that memory management can be more efficient.

Notice circular references