考虑一个问题,为什么在使用timer的时候会碰到这样的代码
- (void)viewDidLoad{
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1 target:self
selector:@selector(timerFire) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:self.timer
forMode:NSDefaultRunLoopMode];
}
- (void)timerFire {
NSLog(@"%s",__func__);
}
很明显,这样的代码,如果不处理好会导致内存泄露,比如要这么处理
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.timer invalidate];
}
如果仅仅是在dealloc 中写[self.timer invalidate]; 是不行,因为控制器会因为循环引用而导致无法释放,从而不会走dealloc
解决方法一:
尝试按照block的方式,使用__weak来添加弱引用
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf
selector:@selector(timerFire) userInfo:nil repeats:YES];
这样的方式还是会导致循环引用.
看timer的文档,我们会得到这样的注解:

the timer maintains a strong reference to this object until it is invalidated;
这句话很明显的告诉使用者,timer 会对target 进行一次 retain操作,从而 timer 和target互相用strong指着,导致无法释放,直到我们主动调用 invalidate.
那么我们如果按照常见的循环引用block来测试的时候,典型的循环引用,self被block捕获而获得一个strong的引用
self.block = ^{
NSLog(@"%@",self);
};
self.block();
破解循环引用
__weak typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%@",weakSelf);
};
self.block();
就可以打破这种循环引用.
其实破解循环引用的原因不在weakself上,而是block的变量捕获机制上,
简单的测试下一段代码转成c++后的代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *objc = [NSObject new];
__weak NSObject *objc_weak = objc;
void (^block)(void) = ^(){
NSLog(@"%@",objc_weak);
};
block();
NSLog(@"Hello, World!");
}
return 0;
}
//相应的block类型 __weak
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *__weak objc_weak;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__weak _objc_weak, int flags=0) : objc_weak(_objc_weak) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//strong的代码
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *objc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_objc, int flags=0) : objc(_objc) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
可以看到在捕获objc时,对应捕获的变量类型,会做出默认strong, 和weak时 使用__weak来保存捕获的变量.而对timer来说,最终会对weakself msgsend retain 其实还是对self 进行retain操作,最终会导致循环引用.
所以在有些代码的时候,我们会这样使用block
__weak typeof(self) weakSelf = self;
self.block = ^{
if (weakSelf) {
__strong typeof(self) strongSelf = weakSelf;
NSLog(@"%@",strongSelf);
}
};
self.block();
首先判断 self是否在block回调的期间被释放,导致self为nil,接着使用strongself 来给weak做一个强指针引用,保证在block的作用域上,self不会被突然释放.