Block 使用总结
2017-06-23 本文已影响55人
Lefe
Block 内存管理:
Block 内存主要分派到 NSGlobalBlock(data area),NSMallocBlock(堆 区 )和 NSConcreteStackBlock(栈区)。在 ARC 环境下,Lefe没有遇到过栈区的 Block,因为 Block 会自动拷贝到栈区。
屏幕快照 2017-06-23 下午10.06.35.png- (void)testBlockMemoryManagerCase1
{
self.logId = @"Hello login";
// __NSGlobalBlock__ 全局,保存到数据区(data area) block
/**
1、当 block 字面量写在全局作用域时,即为 global block;
2、当 block 字面量不获取任何外部变量时,即为 global block;
*/
void(^block1)(void) = ^(){
};
block1();
NSLog(@"block1: %@", block1);
NSLog(@"finsh block: %@", self.finshBlock);
// __NSMallocBlock__ 堆区的block,捕获了变量
/**
当 block 从栈拷贝到堆后,当栈上变量作用域结束时,仍然可以继续使用 block
*/
void(^block2)(void) = ^(){
NSLog(@"Block2: %@", self.logId);
};
block2();
NSLog(@"block2: %@", block2);
/**
_NSConcreteStackBlock 栈区的 block
如果其变量作用域结束,这个 block 就被废弃block 上的 __block 变量也同样会被废弃。
为了解决这个问题,block 提供了 copy 的功能,将 block 和 __block 变量从栈拷贝到堆,就是下面要说的 _NSConcreteMallocBlock。
*/
循环引用一:
屏幕快照 2017-06-23 下午9.44.37.png- (void)testMemoryLeakCase1
{
self.logId = @"Hello logId";
/**
这种情况最容易发现,因为编译器会自动提示出现循环引用
Why?
self(SecondViewController)持有了 finshBlock,你可以把它当作一个普通的属性,是强引用
而 finshBlock 又引用了 self,这样就形成了一个闭环。
How?
既然是因为出现了闭环,我们只需要打破这层闭环就可以,让 finshBlock 持有一个弱引用,这样 self(SecondViewController)持有了 finshBlock,但是 finshBlock 没有持有 self
*/
/**
修改前的:
self.finshBlock = ^(BOOL isSuccess) {
[self loginTest];
};
*/
// __weak typeof(self) weakSelf = self; 一般的宏定义是这样的
__weak SecondViewController *wSelf = self;
self.finshBlock = ^(BOOL isSuccess) {
[wSelf loginTest];
};
/**
在我们的应用中一般是下面这种方式写,为啥使用了 __weak 和 __strong ?
有人可能会问,先 weak 后 strong,那相当于还是强引用了 self,你确定 strong的是 self?
*/
/**
打印:
(lldb) p weakSelf
(SecondViewController *) $0 = 0x0000000101c16f10
(lldb) p self
(SecondViewController *) $1 = 0x0000000101c16f10
(lldb) p strongSelf
(SecondViewController *) $2 = 0x0000000101c16f10
(lldb)
发现 weakSelf self 和 strongSelf 的内存地址是一样的,只是一次浅拷贝;
*/
__weak typeof(self) weakSelf = self;
self.finshBlock = ^(BOOL isSuccess) {
// 如果没有这句话,当 self 被释放后,weakSelf 就变为了空,所以关于 weakSelf 的一些操作也就没什么意义了,如果还想让 weakSelf 所调用的一些方法有意义那么久需要强引用 weakSelf;
__strong typeof(self) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"weakSelf.logId: %@", strongSelf.logId);
NSString *name = strongSelf.logId;
if (name.length > 0) {
NSLog(@"Hello world");
}
[strongSelf loginTest];
});
};
self.finshBlock(YES);
}
循环引用二:
屏幕快照 2017-06-23 下午9.52.27.png- (void)testMemoryLeakCase2
{
/**
这里面出现了两个对象的内存泄漏: task 和 self
task的内存泄漏:
task 有个属性叫 blcok,但是在 block 中又捕获了 task,这样就形成了一个闭环
self 的内存泄漏:
因为这个 block 中捕获了 self,block 没有释放那么 self 咋么能释放呢?
所以只要打破这个闭环,self 就释放了。
*/
AsyncTask *task = [AsyncTask new];
__weak AsyncTask *wTask = task;
task.block = ^(BOOL isFinish) {
NSString *name = wTask.lastLoginId;
self.logId = name;
};
[task sendLogin];
/**
AsyncTask *task = [AsyncTask new];
task.block = ^(BOOL isFinish) {
NSString *name = task;
self.logId = name;
};
[task sendLogin];
*/
}
循环引用三:
屏幕快照 2017-06-23 下午9.44.37.png- (void)testMemoryLeakCase3
{
/**
这里可能不太容易看出来,访问 name 实例变量相当于 self->name
这样 self 持有 finshBlock, finshBlock 持有 self,形成闭环,造成循环引用
*/
__weak SecondViewController *wSelf = self;
self.finshBlock = ^(BOOL isFinish) {
/*
Dereferencing a __weak pointer is not allowed due to possible null value caused by race condition, assign it to strong variable first
*/
// 发现这样写不行,还报错,它的意思是 __weak 指针可能为空,必须要强引用
// wSelf->name = @"Hello lefe";
/**
那么为什么在 testMemoryLeakCase1 中 wSelf.logId = @"Hello logId"; 没有编译错误呢?我想
估计 wSelf.logId 等价于 [wSelf logId],相当于调用了一个方法,
nil 调用方法是没有错误的。你知道属性和实例变量的区别吗?
下面这行代码也会报错的:
__weak AsyncTask *task;
task->_sex;
*/
wSelf.logId = @"Hello logId";
__strong SecondViewController *strongSelf = wSelf;
strongSelf->_name = @"Hello lefe";
};
/**
修改前的代码:
self.finshBlock = ^(BOOL isFinish) {
name = @"Hello lefe";
};
*/
}
```
===== 我是有底线的 ======
[喜欢我的文章,欢迎关注我的新浪微博 Lefe_x,我会不定期的分享一些开发技巧](http://www.weibo.com/5953150140/profile?rightmod=1&wvr=6&mod=personnumber&is_all=1)
![](https://img.haomeiwen.com/i1664496/e409f16579811101.jpg)