当block中涉及self以及self的成员变量时,就会造成循环引用问题。一般解决这类的循环引用是使用__weak和__strong。
案例:
// .h文件
#import <Foundation/Foundation.h>
@interface Pet : NSObject
@property (nonatomic, copy) void(^block)(void);
@end
// .m文件
#import "Pet.h"
@implementation Pet
- (instancetype)init {
if (self = [super init]) {
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
[strongSelf training];
};
}
return self;
}
- (void)training {
NSLog(@"pet training");
}
- (void)dealloc {
NSLog(@"pet dead");
}
@end
// ViewController文件
#import "ViewController.h"
#import "Pet.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
Pet *p = [[Pet alloc] init];
p.block();
}
@end
运行结果为:
2020-03-12 14:14:56.511389+0800 sss[3705:161369] pet training
2020-03-12 14:14:56.511497+0800 sss[3705:161369] pet dead
执行了block里面的内容,并且解决了循环引用问题。
如果block里面的操作是同步执行的,可以省略__strong。将Pet.m的代码修改:
__weak typeof(self) weakSelf = self;
self.block = ^{
[weakSelf training];
};
运行结果为:
2020-03-12 14:29:54.239222+0800 sss[3751:167410] pet training
2020-03-12 14:29:54.239314+0800 sss[3751:167410] pet dead
执行了block里面的内容,并且解决了循环引用问题。
如果block里面的操作是异步执行的,不可以省略__strong。将Pet.m的代码修改:
__weak typeof(self) weakSelf = self;
self.block = ^{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
[weakSelf training];
});
};
运行结果为:
2020-03-12 14:32:59.274722+0800 sss[3781:169249] pet dead
解决了循环引用问题,但是没有执行block里面的内容。因为self提前于block执行之前释放了,ViewController中p提前释放了,p就指向nil了,weakSelf也会被置为nil,当休眠结束时,再执行training方法时,实际上是向nil发送了一个消息。此时,__strong不能省略。
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
[strongSelf training];
});
};
运行结果为:
2020-03-12 14:45:08.079523+0800 sss[3851:174924] pet training
2020-03-12 14:45:08.079750+0800 sss[3851:174924] pet dead
执行了block里面的内容,并且解决了循环引用问题。
如果异步执行使用__strong,weakSelf还会随着实体被释放和置为nil吗?将Pet.m代码进行修改:
- (instancetype)init {
if (self = [super init]) {
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:5];
[strongSelf training];
[weakSelf eat];
});
};
}
return self;
}
- (void)eat {
NSLog(@"pet eat");
}
- (void)training {
NSLog(@"pet training");
}
执行结果为:
2020-03-12 15:01:22.362611+0800 sss[4010:182635] pet training
2020-03-12 15:01:22.362801+0800 sss[4010:182635] pet eat
2020-03-12 15:01:22.362903+0800 sss[4010:182635] pet dead
解决了循环引用问题,并且weakSelf并没有随着实体的释放而置为nil,可以正常调用方法。
推出结论:使用了__strong后,strongSelf也会持有一份weakSelf(不是真正的持有),此时weakSelf不会随着实体的释放而置为nil。strongSelf相当于block中的局部变量,当异步操作结束后,strongSelf会被释放,此时的weakSelf才会被置为nil。解决了循环引用问题,也解决了异步执行问题。
总结:
self:是一个指向实例对象的指针,它的生命周期至少是伴随着当前的实例对象的,所以一旦它和对象之间有循环引用是无法被自动打破的。weakSelf:__weak能解决循环应用问题,但是在异步执行时,可能会因为实体对象释放,造成weak�Self被置为nil,当block中异步执行weakSelf的方法时,其实是向一个nil发送消息。strongSelf:strongSelf也会持有一份weakSelf(不是真正的持有),weakSelf不会因为实体释放被置为nil的时候,只有当局部变量strongSelf执行完方法,出了作用域时,会随着strongSelf一起被置为nil,解决了循环引用问题,也解决了异步执行问题。