Deep understanding of iOS block

auto variable of object type

Example 1

Let's start with a simple example
Define a class YZPerson with only one dealloc method

@interface YZPerson : NSObject
@property (nonatomic ,assign) int age;
@end

@implementation YZPerson

- (void)dealloc
{
 NSLog(@"%s",__func__);
}

@end

Use the following code

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 {
 YZPerson *person = [[YZPerson alloc]init];
 person.age = 10;
 }
 NSLog(@"-----");
 }
 return 0;
}

I think everyone can know what will be output. Yes, person is destroyed first, and then printed - because person is in braces. When the braces are executed, person is destroyed.

iOS-block[1376:15527] -[YZPerson dealloc]
iOS-block[1376:15527] -----

Example two

The above example is not very simple, but the following one,

// Define block
typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 YZBlock block;

 {
 YZPerson *person = [[YZPerson alloc]init];
 person.age = 10;

 block = ^{
 NSLog(@"---------%d", person.age);
 };

 NSLog(@"block.class = %@",[block class]);
 }
 NSLog(@"block Destruction");

 }
 return 0;
}

As shown in the following results, the output shows that when the block is of type \\\\\\\\\\\

iOS-block[3186:35811] block.class = __NSMallocBlock__
iOS-block[3186:35811] block Destruction
iOS-block[3186:35811] -[YZPerson dealloc]

A developer, with a learning atmosphere and a communication circle is particularly important. This is my iOS communication group: 1012951431, sharing BAT, Ali interview questions, interview experience, discussing technology, and sharing learning and growth together! Hope to help developers avoid detours.

Analysis

The terminal executes this line of instructions xcrun - SDK iPhone OS clang - arch arm64 - rewrite objc main.m to generate main.m into main.cpp
You can see the following code

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

Obviously, this block contains YZPerson *person.

block reference instance object under MRC

The above example is not very simple. What about MRC

// Define block
typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 YZBlock block;

 {
 YZPerson *person = [[YZPerson alloc]init];
 person.age = 10;

 block = ^{
 NSLog(@"---------%d", person.age);
 };

 NSLog(@"block.class = %@",[block class]);

 // Under MRC, manual release is required
 [person release];
 }
 NSLog(@"block Destruction");
 // Under MRC, manual release is required
 [block release];
 }
 return 0;
}

The output is

iOS-block[3114:34894] block.class = __NSStackBlock__
iOS-block[3114:34894] -[YZPerson dealloc]
iOS-block[3114:34894] block Destruction

Compared with the above, the difference is that when NSLog(@"block destroy") has not been executed, [YZPerson dealloc] has already been executed. In other words, person leaves the brace and destroys it.

The output shows that when the block is of NSStackBlock type, the block cannot save the person's life

Reference instance object of [block copy] under MRC

Under MRC, the block is copied

// Define block
typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 YZBlock block;

 {
 YZPerson *person = [[YZPerson alloc]init];
 person.age = 10;

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

 NSLog(@"block.class = %@",[block class]);
 // Under MRC, manual release is required
 [person release];
 }

 NSLog(@"block Destruction");
 [block release];
 }
 return 0;

The output result is that when the block is of type \\\\\\\\\\

iOS-block[3056:34126] block.class = __NSMallocBlock__
iOS-block[3056:34126] block Destruction
iOS-block[3056:34126] -[YZPerson dealloc]

__weak modification

  • The following code
// Define block
typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 YZBlock block;

 {
 YZPerson *person = [[YZPerson alloc]init];
 person.age = 10;

 __weak YZPerson *weakPerson = person;

 block = ^{
 NSLog(@"---------%d", weakPerson.age);
 };

 NSLog(@"block.class = %@",[block class]);
 }

 NSLog(@"block Destruction");
 }
 return 0;
}
  • Output is
iOS-block[3687:42147] block.class = __NSMallocBlock__
iOS-block[3687:42147] -[YZPerson dealloc]
iOS-block[3687:42147] block Destruction
  • Generate cpp file

  • Be careful:

  • When using clang to convert OC to C + + code, you may encounter the following problems
    cannot create __weak reference in file using manual reference

  • Solution: support ARC and specify the runtime system version, such as
    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

After generation, you can see that the following codes are generated significantly in MRC, because ARC automatically performs copy operation

//copy function
 void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);

 //dispose function
 void (*dispose)(struct __main_block_impl_0*);
struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 //weak modification
 YZPerson *__weak weakPerson;
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
 impl.isa = &_NSConcreteStackBlock;
 impl.Flags = flags;
 impl.FuncPtr = fp;
 Desc = desc;
 }
};

static struct __main_block_desc_0 {
 size_t reserved;
 size_t Block_size;
 //copy function
 void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);

 //dispose function
 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
};

//The copy function internally calls the block object assign function
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {

//asssgin makes strong or weak references to objects
_Block_object_assign((void*)&dst->person, 
(void*)src->person, 
3/*BLOCK_FIELD_IS_OBJECT*/);
}

//The dispose function calls the block object dispose function internally
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->person, 
3/*BLOCK_FIELD_IS_OBJECT*/);
}

Summary

Whether it's MAC or ARC

  • When the block is of type \\\\\\\\\\\\
  • When block is of type \\\\\\\\\\\\\\.

In fact, it's also easy to understand, because the block itself is on the stack, and it can disappear at any time. How can we protect other people's lives?

  • When the auto variable of the object type is accessed inside the block

    • If the block is on the stack, there will be no strong reference to the auto variable
  • If the block is copied to the heap

    • The copy function inside the block will be called
    • The copy function internally calls the block object assign function
    • _The block object assign function will perform corresponding operations according to the auto variable modifiers (strong, weak, unsafe, un retain ed) to form a strong or weak reference
  • If the block is removed from the heap

    • The dispose function inside the block will be called
    • The dispose function calls the block object dispose function internally
    • _The block object dispose function automatically releases the referenced auto variable (release)
function Timing of invocation
copy function Block on stack copied to heap
dispose function When block s on the heap are discarded

__block

To start with a simple example, look at the following code

// Define block
typedef void (^YZBlock)(void);

int age = 10;
YZBlock block = ^{
 NSLog(@"age = %d", age);
};
block();

The code is very simple. After running, output

age = 10

The above example accesses the external local variables in the block, so the problem comes. If you want to modify the external local values in the block, how do you do it?

Three methods of modifying local variables

Write as global variable

We define a as a global variable, so it can be accessed everywhere,

// Define block
typedef void (^YZBlock)(void);
 int age = 10;

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 YZBlock block = ^{
 age = 20;
 NSLog(@"block After internal modification age = %d", age);
 };

 block();
 NSLog(@"block End of call age = %d", age);
 }
 return 0;
}

This is very simple. The output is

age = 20 after block internal modification
 age = 20 after block call

There is no problem with the output, because global variables are accessible everywhere, and the memory address of age can be directly operated inside the block. After the block is called, the value of the address pointed to by the global variable age has been changed to 20, so it is the above print result

static modify local variable

// Define block
typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {
 @autoreleasepool {
 static int age = 10;
 YZBlock block = ^{
 age = 20;
 NSLog(@"block After internal modification age = %d", age);
 };

 block();
 NSLog(@"block End of call age = %d", age);
 }
 return 0;
}

The output of the above code is

age = 20 after block internal modification
 age = 20 after block call

The terminal executes this line of instructions xcrun - SDK iPhone OS clang - arch arm64 - rewrite objc main.m to generate main.m into main.cpp
You can see the following code

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

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

 (*age) = 20;
 NSLog((NSString *)&__NSConstantStringImpl__var_folders_x4_920c4yq936b63mvtj4wmb32m0000gn_T_main_5dbaa1_mi_0, (*age));
}

It can be seen that when the local variable is decorated with static, there will be an int *age member in the block, that is to say, the address of age will be captured. In this way, of course, the local variable age can be modified inside the block.

  • Although the above two methods can achieve the purpose of modifying local variables in the block, doing so will cause the memory to be unable to be released. Whether global variables or static variables are decorated, they cannot be destroyed in time and will always exist in memory. Most of the time, we just need to use it temporarily. When we don't use it, we can destroy it. Then the third one, the protagonist of today, is the grand debut of the block

__block to decorate

The code is as follows

// Define block
typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {
 @autoreleasepool {
 __block int age = 10;
 YZBlock block = ^{
 age = 20;
 NSLog(@"block After internal modification age = %d",age);
 };

 block();
 NSLog(@"block End of call age = %d",age);
 }
 return 0;
}

The output is the same as the above two

age = 20 after block internal modification
 age = 20 after block call

__block analysis

  • The terminal executes this line of instructions xcrun - SDK iPhone OS clang - arch arm64 - rewrite objc main.m to generate main.m into main.cpp

First of all, we can find many "block" and "byref" age "structures

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 // There are more structures of type "block" byref "age"
 __Block_byref_age_0 *age; // by ref
 // fp is the function address desc is the structure of description information block byref age 0 * age flags flag
 __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; //fp is the function address
 Desc = desc;
 }
};

If you look at the structure block byref age 0, you can see that the first member variable is an isa pointer, and the second is a pointer to itself forwarding

// Structure block byref age 0
struct __Block_byref_age_0 {
 void *__isa; //isa pointer
 __Block_byref_age_0 *__forwarding; // Pointer to itself
 int __flags;
 int __size;
 int age; //Usage value
};

Check the code in the main function

 // This is the original code block byref age 0
 __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {
 (void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};

// This is the original block code
YZBlock block = ((void (*)())&__main_block_impl_0(
(void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

The code is too long. Simplify it and remove some strong conversion codes. The results are as follows

// This is the original code block byref age 0
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};

//This is the simplified code block byref age 0
__Block_byref_age_0 age = {
 0, //Assign to  isa
 (__Block_byref_age_0 *)&age,//Assign to "forwarding", which is its own pointer
 0, // Assign to flag
 sizeof(__Block_byref_age_0),//Assign to size
 10 // age usage value
 };

// This is the original block code
YZBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));

// This is the simplified block code
YZBlock block = (&__main_block_impl_0(
 __main_block_func_0,
 &__main_block_desc_0_DATA,
 &age,
 570425344));

 ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
 //Simplify to
block->FuncPtr(block);

Where the second (? Block? Byref? Age? 0 *) & age in the block? Byref? Age? 0 structure is assigned to the second? Block? Byref? Age? 0 *? Forwarding in the above code structure, so the pointer to itself is stored in the forwarding

//This is the simplified code block byref age 0
__Block_byref_age_0 age = {
 0, //Assign to  isa
 (__Block_byref_age_0 *)&age,//Assign to "forwarding", which is its own pointer
 0, // Assign to flag
 sizeof(__Block_byref_age_0),//Assign to size
 10 // age usage value
 };

The code in the structure block byref age 0 is as follows. The second forwarding stores the pointer to itself, and the fifth age stores the local variable

// Structure block byref age 0
struct __Block_byref_age_0 {
 void *__isa; //isa pointer
 __Block_byref_age_0 *__forwarding; // Pointer to itself
 int __flags;
 int __size;
 int age; //Usage value
};

When calling, first find the pointer through "forwarding", and then fetch the age value.

(age->__forwarding->age));

 

 

 

 

 

 

Summary

  • __Block can be used to solve the problem that the value of auto variable cannot be modified inside the block

  • __block cannot decorate global and static variables

    • The compiler wraps the block variable as an object

Called to find the memory of age from the pointer of block byref age 0 and modify the value

 

 

 

Memory management issues

bloc access OC object

The code is as follows

When the block internal accesses the external OC object

eg:

// Define block
typedef void (^YZBlock)(void);

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 NSObject *obj = [[NSObject alloc]init];
 YZBlock block = ^{
 NSLog(@"%p",obj);
 };
 block();
 }
 return 0;
}

Using clang to convert OC to C + + code in terminal

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

 

 

 

Because it is under ARC, it will copy. The stack will be copied to the heap. There are copy and dispose in the main block desc 0 structure

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

Copy calls main block copy 0

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

The internal block object assign will make strong or weak reference to it according to the modifier strong or weak in the code.

View main block impl 0

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 //Strong strong reference
 NSObject *__strong obj;
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__strong _obj, int flags=0) : obj(_obj) {
 impl.isa = &_NSConcreteStackBlock;
 impl.Flags = flags;
 impl.FuncPtr = fp;
 Desc = desc;
 }
};

You can see that the modifier is strong, so when you call block object assign, it will be strongly referenced.

From the front

  • When a block is on the stack, there is no strong reference to the block variable

  • When a block is copied to the heap

    • The copy function inside the block will be called
    • The copy function internally calls the block object assign function
    • _The block object assign function forms a strong reference (retain) to the block variable
  • When a block is removed from the heap

    • The dispose function inside the block will be called
    • The dispose function calls the block object dispose function internally
    • _The block object dispose function automatically releases the referenced block variable (release)

Copy

When copying,

  • The copy function inside the block will be called

    • The copy function internally calls the block object assign function
    • _The block object assign function forms a strong reference (retain) to the block variable

    As we know, the following code

__block int age = 10;
 YZBlock block = ^{
 age = 20;
 NSLog(@"block After internal modification age = %d",age);
 };

The local variable age is on the stack and refers to age within the block. But when the block is copied from the stack to the heap, how can it be accessed the next time the block accesses age? Because we know that local variables on the stack will be destroyed at any time.

Let's say there are two blocks on the stack, block0 and block1. At the same time, the \. Now to copy block0, we know that if we copy the block on the stack, it will be copied to the heap, that is to say, block0 will be copied to the heap. Because block0 holds the \\\\\\\\\\\\\\\.

 

 

 

In the above example, just now block0 has been copied to the heap. Now if block1 has also been copied to the heap, because the variables just copied to the heap, you don't need to copy again. Just make the block1 on the heap strongly refer to the variables on the heap.

 

 

 

release

When released

  • The dispose function inside the block will be called
    • The dispose function calls the block object dispose function internally
    • _The block object dispose function automatically releases the referenced block variable (release)

In the above code, if only one block references the block variable on the heap, the block variable on the heap will be destroyed directly when the block is destroyed, but if two blocks reference the block variable, the block variable will be discarded only when both blocks are abandoned.

 

 

 

Actually, in the final analysis, it's who uses it and who is responsible for it

auto variable and block variable of object type

Put all the previous ones together. There are auto variables num, block variables int, obj and weakobj 2 as follows

 __block int age = 10;
 int num = 8;
 NSObject *obj = [[NSObject alloc]init];
 NSObject *obj2 = [[NSObject alloc]init];
 __weak NSObject *weakObj2 = obj2;
 YZBlock block = ^{
 NSLog(@"age = %d",age);
 NSLog(@"num = %d",num);
 NSLog(@"obj = %p",obj);
 NSLog(@"weakObj2 = %p",weakObj2);
 NSLog(@"block After internal modification age = %d",age);
 };

block();

Execute terminal instruction

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

The generated code is as follows

 

 

 

Object type decorated by block

  • When the block variable is on the stack, there is no strong reference to the pointed object

  • When the block variable is copied to the heap

    • The copy function inside the block variable is called
    • The copy function internally calls the block object assign function
    • _The block object assign function will perform the corresponding operations according to the modifiers (strong, weak, unsafe and unretained) of the pointed object to form a strong reference (retain) or a weak reference (Note: This is limited to ARC, not MRC)
  • If the block variable is removed from the heap

    • The dispose function inside the block variable is called
    • The dispose function calls the block object dispose function internally
    • _The block object dispose function automatically releases the object it points to (release)

__Forward pointer of block

//There is "forwarding" in the structure "block" byref "obj" 0
 struct __Block_byref_obj_0 {
 void *__isa;
 __Block_byref_obj_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSObject *__strong obj;
};

// During the visit
age->__forwarding->age

Why not use age directly, but age - >?

This is because if the block variable is on the stack, it can be accessed directly, but if it has been copied to the heap, and when it is accessed, it also accesses the stack, there will be a problem. Therefore, first find the address on the heap according to the forwarding, and then take the value

 

 

 

summary

  • When block s are on the stack, there is no strong reference to them

  • When block s are copied to the heap, they are processed through the copy function

    • __block variable (assuming the variable name is a)
  • _Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

  • auto variable of object type (assuming that the variable name is p)
    _Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

  • When block s are removed from the heap, they are released through the dispose function
    __block variable (assuming the variable name is a)
    _Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);

  • auto variable of object type (assuming that the variable name is p)
    _Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);

Circular reference problem

Continue to explore the circular reference problem of block.

Look at the following code. There is a Person class with two properties, block and age

#import <Foundation/Foundation.h>

typedef void (^YZBlock) (void);

@interface YZPerson : NSObject
@property (copy, nonatomic) YZBlock block;
@property (assign, nonatomic) int age;
@end

#import "YZPerson.h"

@implementation YZPerson
- (void)dealloc
{
 NSLog(@"%s", __func__);
}
@end

The following code in main.m

int main(int argc, const char * argv[]) {
 @autoreleasepool {

 YZPerson *person = [[YZPerson alloc] init];
 person.age = 10;
 person.block = ^{
 NSLog(@"person.age--- %d",person.age);
 };
 NSLog(@"--------");

 }
 return 0;
}

Output only

iOS-block[38362:358749] -–

That is to say, when the program ends, the person is not released, resulting in a memory leak.

Circular reference reason

The following line of code has a person pointer to the YZPerson object

YZPerson *person = [[YZPerson alloc] init];

Execution

person.block = ^{
 NSLog(@"person.age--- %d",person.age);
 };

After that, there is a strong pointer inside the block to person, and the following code generates cpp file

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m

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

And block is the property of person

@property  (copy, nonatomic) YZBlock block;

When the program exits, the local variable person is destroyed, but because mjpherson and block directly and strongly reference each other, no one can release it.

 

 
i

 

__Week resolve circular references

In order to solve the above problems, you only need to use "break" to modify

int main(int argc, const char * argv[]) {
 @autoreleasepool {
 YZPerson *person = [[YZPerson alloc] init];
 person.age = 10;

 __weak YZPerson *weakPerson = person;

 person.block = ^{
 NSLog(@"person.age--- %d",weakPerson.age);
 };
 NSLog(@"--------");

 }
 return 0;
}

After compilation is complete

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 // Weak reference to weakPerson inside block
 YZPerson *__weak weakPerson;
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
 impl.isa = &_NSConcreteStackBlock;
 impl.Flags = flags;
 impl.FuncPtr = fp;
 Desc = desc;
 }
};

When the local variable disappears, for YZPseson, there is only one pointer to it. If the pointer points to it, it will be destroyed, and then the block will also be destroyed.

__Unsafe

In addition to the above "break", you can also use "unsafe" unretained to solve circular references

int main(int argc, const char * argv[]) {
 @autoreleasepool {
 YZPerson *person = [[YZPerson alloc] init];
 person.age = 10;

 __unsafe_unretained YZPerson *weakPerson = person;

 person.block = ^{
 NSLog(@"person.age--- %d",weakPerson.age);
 };
 NSLog(@"--------");

 }
 return 0;
}

The cpp file for is

struct __main_block_impl_0 {
 struct __block_impl impl;
 struct __main_block_desc_0* Desc;
 YZPerson *__unsafe_unretained weakPerson;
 __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, YZPerson *__unsafe_unretained _weakPerson, int flags=0) : weakPerson(_weakPerson) {
 impl.isa = &_NSConcreteStackBlock;
 impl.Flags = flags;
 impl.FuncPtr = fp;
 Desc = desc;
 }
};

Although it is possible to resolve circular references, it is better not to use because

  • __weak: no strong reference will be generated. When the object pointed to is destroyed, the pointer will be set to nil automatically
  • __unsafe_unretained: no strong reference will be generated. When the pointed object is destroyed, the address value stored by the pointer will not be changed

__block resolves circular references

eg:

int main(int argc, const char * argv[]) {
 @autoreleasepool {
 __block YZPerson *person = [[YZPerson alloc] init];
 person.age = 10;
 person.block = ^{
 NSLog(@"person.age--- %d",person.age);
 //This sentence is indispensable
 person = nil;
 };
 // Must be called once
 person.block();
 NSLog(@"--------");
 }
 return 0;
}

In the above code, circular references can also be solved. Note, however, that person.block(); must be called once in order to execute person = nil

The corresponding results are as follows

  • In the following code, a block makes a strong reference to a block
__block YZPerson *person = [[YZPerson alloc] init];
person.block = ^{
 NSLog(@"person.age--- %d",person.age);
 //This sentence is indispensable
 person = nil;
};
  • The person object itself is a strong reference to a block
@property  (copy, nonatomic) YZBlock block;
  • __block makes strong reference to person
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*);
 //`__block ` makes strong reference to person
 YZPerson *__strong person;
};

So their reference relationship is shown in the figure

 

 

 

When person = nil is executed, the block dereferences the person and, in turn, releases all of them.
But you must call person = nil, otherwise, you cannot dereference the loop

Summary

Through the previous analysis, we know that under ARC, the best way to compare the above three methods is "weak"

Notes under MRC

In MRC, because weak pointer is not supported, only unsafe unretained or block can be used to solve circular reference

End

Back to the beginning

  • What is the principle of block? What is the essence?

  • __What is the role of block? What are the precautions for use?

  • Why is the attribute modifier of block copy? What are the notes for using block?

  • Once the block is not copied, it will not be on the heap

  • Block is modifying NSMutableArray. Do you need to add block?

Do you have your own answer now?

In addition, if you want to advance together, you can add a communication group 1012951431 , choose to join in communication and study together. Looking forward to your joining!

Tags: iOS SDK Mac Attribute

Posted on Tue, 10 Mar 2020 23:42:26 -0700 by ttroutmpr