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
-
- Located in global area
-
- 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)
- Manually copy
- Block as the return value or argument
- Strongly referenced or modified by Copy
- 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.