1、引发内存泄露
假设一个类有两个属性:block和name;
//
// PHBlockTest.h
// BlockTestTest
//
// Created by 項普華 on 2017/2/9.
// 邮箱: xiangpuhua@126.com
// 电话: +86 13316987488
// 主页: https://github.com/xphaijj
// Copyright © 2017年 項普華. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface PHBlockTest : NSObject
@property (nonatomic, copy) void(^block)();
@property (nonatomic, copy) NSString *name;
@end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
//
// PHBlockTest.m
// BlockTestTest
//
// Created by 項普華 on 2017/2/9.
// 邮箱: xiangpuhua@126.com
// 电话: +86 13316987488
// 主页: https://github.com/xphaijj
// Copyright © 2017年 項普華. All rights reserved.
//
@implementation PHBlockTest
- (id)init
{
if (self = [super init]) {
self.block = ^{
NSLog(@"%@",_name);
NSLog(@"%@",self->_name);
NSLog(@"%@",self.name);
};
}
return self;
}
@end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
普通情况下,block是存在于栈中的,意味着其生命周期由系统管理,不需要我们手动管理。这就存在一个问题,就是我们如果使用block保存一段代码块,在回调的时候,说不定已经被系统回收了。
因此我们需要想办法将block存储到堆中,方法就是使用copy,做一次复制。(如果使用retain的话,只会将其计数器加一,多做一次强引用,不会重新分类新的内存,所以依然存在于栈中)。 如上代码块中的m文件中的
NSLog(@"%@",_name);
NSLog(@"%@",self->_name);
NSLog(@"%@",self.name);
- 1
- 2
- 3
,此时如果在block中调用持有它的对象,就会产生循环引用,造成内存泄露。这个问题怎么引起的呢? 因为block存在于堆中,在其代码块中引用的对象都会产生一个强指针,而这个时候block本身就是被其引用的对象(copy)强指针指着。这样就造成了双方都无法释放,从而造成内存泄露。解决方案如下:
//
// PHBlockTest.m
// BlockTestTest
//
// Created by 項普華 on 2017/2/9.
// 邮箱: xiangpuhua@126.com
// 电话: +86 13316987488
// 主页: https://github.com/xphaijj
// Copyright © 2017年 項普華. All rights reserved.
//
@implementation PHBlockTest
- (id)init
{
if (self = [super init]) {
__unsafe_unretained PHBlockTest *temp = self;
//__weak PHBlockTest *temp = self;
self.block = ^{
NSLog(@"%@", temp->_name);
NSLog(@"%@", temp.name);
};
}
return self;
}
@end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
很多时候我使用的是下面的代码:
__weak PHBlockTest *temp = self;
- 1
和上面的
__unsafe_unretained PHBlockTest *temp = self;
- 1
有什么区别呢?
简单说就是,unsafe,为什么不安全呢?因为当其所修饰的对象释放时,它并不知道,所以,其地址依然存在,不会清为nil,这样第七行代码
temp->_name
是可以访问的(在使用时也应当注意这个细节),而使用weak修饰的话,当其修饰的对象被释放时,temp会被清为nil,这样就会出现nil->_name的情况.这是不允许的.
如果想要了解
__unsafe_unretained和__weak的具体区别的话,可查阅其它资料.
为了防止上面所说的weak释放问题还有下面一种写法:
//
// PHBlockTest.m
// BlockTestTest
//
// Created by 項普華 on 2017/2/9.
// 邮箱: xiangpuhua@126.com
// 电话: +86 13316987488
// 主页: https://github.com/xphaijj
// Copyright © 2017年 項普華. All rights reserved.
//
@implementation PHBlockTest
- (id)init
{
if (self = [super init]) {
__weak PHBlockTest *temp = self;
self.block = ^{
__strong PHBlockTest *strongSelf = temp;//防止在下面使用temp的时候 temp被释放掉
NSLog(@"%@", strongSelf->_name);
NSLog(@"%@", strongSelf.name);
};
}
return self;
}
@end