block分类
- 全局block
__NSGlobalBlock__
:位于全局区,在Block内部不使用外部变量,或者只使用静态变量和全局变量 - 栈区block
__NSStackBlock__
:位于堆区,在Block内部使用变量或者OC属性,并且赋值给强引用或者Copy修饰 - 堆区block
__NSMallocBlock__
:位于栈区,Block内部使用变量或者OC属性,但不能赋值给强引用或者Copy修饰
int a = 10;
void (^blockA)(void) = ^{
};
void (^blockB)(void) = ^{
NSLog(@"Cooci - %d",a);
};
void (^__weak blockC)(void) = ^{
NSLog(@"Cooci - %d",a);
};
NSLog(@"----------------:%@",blockA);
NSLog(@"----------------:%@",blockB);
NSLog(@"----------------:%@",blockC);
image.png
在
ARC
环境下,需要在block
前进行__weak
修饰,并且调用外部变量,才会是__NSStackBlock__
block引用计数
- NSGlobalBlock:,存储于全局数据区,由系统管理其内存,retain、copy、release操作都无效。如果访问了外部static或者全局变量也是这种类型。
- NSStackBlock:retain、release操作无效,存储于栈区,变量作用域结束时,其被系统自动释放销毁。
- NSMallocBlock:支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数,当引用计数为零的时候释放。
NSObject *objc = [NSObject new];
NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
void(^strongBlock)(void) = ^{
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
strongBlock();
void(^__weak weakBlock)(void) = ^{ // + 1
NSLog(@"---%ld",CFGetRetainCount((__bridge CFTypeRef)(objc)));
};
weakBlock();
void(^mallocBlock)(void) = [weakBlock copy];
mallocBlock();
image.png
这里的正确答案为
1,3,4,5
,这里我们就有疑问了,为什么第二步引用计数直接加了2
呢?
- 在
strongBlock
中,首先先要捕获外部变量objc
,这时候引用计数+1
,然后由栈区copy到堆区
,引用计数再+1
,也就变为了3
。 -
weakBlock
被__weak
修饰,所以是个栈区block,只是捕获外部变量+1
,引用计数变为了4
-
mallocBlock
进行了一步[weakBlock copy]
,相当于栈区copy到堆区
,引用计数+1
,引用计数变为了5
block堆栈释放差异
案例1
int a = 0;
void(^__weak weakBlock)(void) = nil;
{
void(^__weak strongBlock)(void) = ^{
NSLog(@"---89899:%d", a);
};
weakBlock = strongBlock;
NSLog(@"1 - %@ - %@",weakBlock,strongBlock);
}
weakBlock();
image.png
-
weakBlock
是个栈block
- 在代码块中,定义
strongBlock
为栈block
- 将
strongBlock
赋值给weakBlock
,此时两个block均指向同一个栈block
- 因为两个
栈block
的生命周期到方法结束 - 所以
weakBlock
能够正常调用
案例2
int a = 0;
void(^__weak weakBlock)(void) = nil;
{
void(^strongBlock)(void) = ^{
NSLog(@"---89899:%d", a);
};
weakBlock = strongBlock;
NSLog(@"1 - %@ - %@",weakBlock,strongBlock);
}
weakBlock();
image.png
该案例是对案例1
进行了小修改,将strongBlock
改为堆block
-
weakBlock
是个栈block
- 在代码块中,定义
strongBlock
为堆block
- 将
strongBlock
赋值给weakBlock
,进行指针,此时两个block均指向同一个堆block
,但是没有强引用这个block
- 代码块执行完毕后,该NSMallocBlock就会被释放,此时weakBlock指向的对象已经被释放,形成野指针,所以无法正常执行。
接下来,将weakBlock和NSMallocBlock都去掉__weak
,则能够正常执行,因为在对weakBlock进行了赋值时,weakBlock对堆中的block进行了强引用。所以代码块运行完后不会释放,能够正常运行
block循环引用
self.name = @"SH";
self.block = ^(void){
NSLog(@"%@",self.name);
};
self.block();
上述这种情况,会造成循环引用
- self持有持有block
- 在block内部持有self
- 这样就造成了vc无法调用dealloc,而block接收不到release信号,导致retainCount不等于0,从而也无法释放
循环引用解决方法
-
weak
使用
self.name = @"SH";
__weak typeof(self) weakSelf = self;
self.block = ^(void){
NSLog(@"%@",weakSelf.name);
};
self.block();
这个时候self持有block,而block弱引用self
,弱引用会自动变为nil,强持有中断,所以不会引起循环引用。
但是这样还会存在一个问题
这里虽然没有引起循环引用,但是
block
延迟2秒执行,这样就造成了vc
释放,而block无法获取vc
中的属性。
- 强弱共舞
self.name = @"SH";
__weak typeof(self) weakSelf = self;
self.block = ^(void){
__strong __typeof(weakSelf)strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
self.block();
这里在block
添加__strong __typeof(weakSelf)strongSelf = weakSelf;
,添加strongSelf
后这里的持有关系就变为了self->block-> strongSelf-> weakSelf->self
,这里weakSelf
被强引用了就不会被自动是释放,但是strongSelf
是个临时变量,生命周期只在block
内部,block
执行完毕后strongSelf
会被释放,而weakSelf
也会被释放
- 手动中断持有关系
__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();
这里的持有关系为self-> block ->vc->self
,当vc在block中被置为nil后,这个环就被打破了,也就不会造成循环引用
- block传参
self.name = @"SH";
self.block = ^(ViewController *vc){
NSLog(@"%@",vc.name);
};
self.block(self);
将self
作为参数传入block中,这里是对self
的指针进行拷贝,只是指向同一片内存,并没有对self
进行持有
循环引用案例
- 静态变量持有
//全局声明
static ViewController *staticSelf_;
- (void)blockWeak_static {
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
}
这种情况会导致循环引用,weakSelf虽然是弱引用,但是staticSelf_静态变量,并对weakSelf进行了持有,staticSelf_释放不掉,所以weakSelf也释放不掉!导致循环引用!
- __strong持有问题
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@"%@", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
}
这种情况会引起循环引用
,在doWork内部,__strong typeof(self) strongSelf = weakSelf;doStudent这个内部block调用了外部变量,所以他会从栈block copy到堆中,从而导致strongSelf的引用计数增加,无法释放掉,进而导致循环引用!
网友评论