This section analyzes some instances of block, classification of block, circular reference, etc. Get an idea of the underlying structure of the block.Copy the code

The classification of the block

  • NSGlobalBlock
      1. Located in global area
      1. Use no external variables inside a Block, or use only static and global variables
static int a = 12;

void (^globalBlock)(void) = ^{

    NSLog(@"---%d", a);

};
// <__NSGlobalBlock__: 0x1082af100>
NSLog(@"% @",globalBlock);
Copy the code
  • NSMallocBlock
    • Located in the heap area
    • Use variables or OC attributes inside blocks and assign values to strongly referenced or copy-modified variables
int a = 10;

void (^block)(void) = ^{

    NSLog(@"haha - %d",a);

};

NSLog(@"% @",block);
Copy the code
  • NSStackBlock
    • Located in the stack area
    • As with mallocblocks, local variables or OC attributes can be used internally. But you cannot assign to strongly referenced or copy-modified variables
int a = 10;

void (^__weak block)(void) = ^{

    NSLog(@"haha - %d",a);

};

NSLog(@"% @",block);
Copy the code

Four cases where stack blocks are copied to heap blocks (note that blocks contain variables or oc attributes)

  1. Manually copy
  2. Block as the return value or argument
  3. Strongly referenced or modified by Copy
  4. The system API contains usingBlock

Capture external variables – Reference counting processing of external variables

NSObject *objc = [NSObject new];

NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)(objc))); / / 1
 // Block source code
 // Capture + 1
 / block/heap area
 // stack - memory -> heap + 1
void(^strongBlock)(void) = ^{ 

    NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));/ / 3

};

strongBlock();

void(^__weak weakBlock)(void) = ^ {/ / + 1

    NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));/ / 4

};

weakBlock();

void(^mallocBlock)(void) = [weakBlock copy];/ / 5

mallocBlock();
Copy the code

for

void(^strongBlock)(void) = ^{ 

    NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));/ / 3

};
Copy the code

Is equal to the


void(^__weak weakBlock)(void) = ^ {/ / + 1

    NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));/ / 4

};

weakBlock();

void(^mallocBlock)(void) = [weakBlock copy];/ / 5
Copy the code

The effect of these two things, one is to capture the variable +1, and then copy it onto the heap +1, so it’s 3. This will be covered in the next source code analysis section

A preliminary study of block copy

We know that block is copy, of course arc copy is the same as strong, but why copy, why copy blocks to the heap let’s see a demo first

- (void)blockDemo1{

    int a = 0;

    void(^ __weak weakBlock)(void) = ^{

        NSLog(@"-----%d", a);

    };
    //_DWBlock is a simulated source code block encapsulation
    struct _DWBlock *blc = (__bridge struct _DWBlock *)weakBlock;

    // Depth copy

    id __strong strongBlock = weakBlock;

    blc->invoke = nil;

    void(^strongBlock1)(void) = strongBlock;

    strongBlock1();


}
Copy the code

The demo will crash because we set BLC invoke to nil, so it will not be found when we call it next, so an error will be reportedWeakBlock and StrongBlock above are both stack blocks if changedid __strong strongBlock = [weakBlock copy];It works fine, it copies the block, strongBlock is a block on the heap, so it changes the BLC but it doesn’t have anything to do with strongBlock.

demo2

- (void)blockDemo3{

// Stack memory translation

NSObject *a = [NSObject alloc];

void(^__weak weakBlock)(void) = nil;

{// a block of code


    void(^__weak strongBlock)(void) = ^{

        NSLog(@"% @" -- -, a);

    };

    weakBlock = strongBlock;

    NSLog(@"1 - %@ - %@",weakBlock,strongBlock);

    }

    weakBlock();

}
// Print the result
//**1 - <__NSStackBlock__: 0x7ffee5f47450> - <__NSStackBlock__: 0x7ffee5f47450>**
//**---(null)**
Copy the code

We know that on the stack, memory is reclaimed by the system, and the scope of weakBlock() is within the whole function, so weakBlock() will be executed

We put thevoid(^__weak strongBlock)(void) = ^{Instead ofvoid(^ strongBlock)(void) = ^{It will crash and output1 - <__NSMallocBlock__: 0x600003334840> - <__NSMallocBlock__: 0x600003334840>The collapse inweakBlock()-Thread 1: EXC_BAD_ACCESS (code=1, address=0x10); At this time, it is the heap block. We see that after the code block, strongBlock is gone, and weakBlock is empty, so it will crash in the later call.

A circular reference to a Block

We know that most of the time, A holds B, and when A frees, B frees, and in some cases, A holds B, and B holds A, and then it creates A reference loop. Eg:

@property (nonatomic, strong) DWBlock block;

@property (nonatomic, copy) NSString *name;

self.block = ^{

    NSLog(@"% @", self.name);

};
self.block();
Copy the code

Print NSLog inside dealloc (@” Dealloc is coming “); Notice that when the page exits, it does not go to dealloc. The familiar scenario is that self holds a block, and within the block holds self, creating a circular reference. We all know to add __weak to break circular references

__weak typeof(self) weakSelf = self;

self.block = ^{

    NSLog(@"% @", weakSelf.name);

};
Copy the code

What happens if there is a time-consuming operation in the block

__weak typeof(self) weakSelf = self;

self.block = ^{

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    NSLog(@"% @", weakSelf.name);

    });

};

self.block();

/ / dealloc to * * * *
//**(null)**
Copy the code

At this time, there will be problems. Dealloc is first gone, weakSelf is released, and the printed value is not accurate.

Of course, we can carry out a strong hold on weakSelf to ensure the accuracy of printing.

__weak typeof(self) weakSelf = self;

self.block = ^{

    __strong typeof(self) strongSelf = weakSelf;

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    NSLog(@"% @", strongSelf.name);

    });

};

self.block();
Copy the code

__strong typeof(self) strongSelf = weakSelf; Although it is a strong reference, it is a local variable and will be recycled when it is out of scope, so there is no circular reference in this case. Circular references are also handled on a case-by-case basis, and __strong is sometimes used to keep objects from being recycled.

We can also break circular references by setting self to nil when appropriate

__block ViewController *vc = self;

self.block = ^(void){

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        NSLog(@"% @",vc.name);

        vc = nil;

        });

};

self.block();
Copy the code

The reason for the previous circular reference is that the block holds the variable inside, so we treat vc as a parameter, can cleverly avoid this problem

self.block = ^(ViewController *vc){

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    NSLog(@"% @",vc.name);

    });

    };

self.block(self);
Copy the code

We have described three ways to break circular references above, but let’s examine whether the following example produces circular references. Eg1:

static ViewController *staticSelf_;

- (void)blockWeak_static {

    // It is the same memory space

    __weak typeof(self) weakSelf = self;

    staticSelf_ = weakSelf;

    // staticSelf_ -> weakSelf -> self

}
Copy the code

Because self is held indirectly by a static variable and is not reclaimed when the page exits, a circular reference occurs

- (void)block_weak_strong {

    __weak typeof(self) weakSelf = self;

    self.doWork = ^{

    __strong typeof(self) strongSelf = weakSelf;

    weakSelf.doStudent = ^{

    NSLog(@"% @", strongSelf);

    };

    weakSelf.doStudent();

    };

    self.doWork();

}
Copy the code

__strong typeof(self) strongSelf = weakSelf; StrongSelf is a temporary variable, but Weakself. doStudent has a hold on strongSelf, which causes its reference count to +1. It holds weakSelf, and weakSelf holds self, when the page exits, The reference count inside it cannot yet be cleared for recycling, so it does not dealloc.