1.block类型
1.1__NSGlobalBlock__ :全局区的 (没有引用外部变量)
应用场景:提示语提示用户或者保存数据等其他情况,不做逻辑操作
[Person personWithName:^(NSString *personName) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:personName delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}];
没有使用外部变量,此时的 alert 变量是放在 栈区 ,block执行完即释放,类似的情况还有AFNetWorking里failure:^(NSError *error){#提示语#} 不用担心循环引用
1.2__NSStackBlock__ :栈区 (内部使用了外部变量)
应用场景:做逻辑操作,赋值,遍历数组或者动画等
[Person personWithName:^(NSString *personName) {
self.name = personName;
NSLog(@“%@",self.name);
}];
使用了外部变量_name属性,此时就成为了栈Block,类似的还有-(void)enumerateObjectsUsingBlock:(void (^)(id _Nonnull, NSUInteger, BOOL * _Nonnull))block 和 +(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations 都是 栈Block,不用担心循环引用,下面我会来证实一下,像平时自己写的方法中出现的block,都是栈block,不用担心循环引用
![](https://img.haomeiwen.com/i6954301/3e081bc3141d4578.png)
1.3__NSMallocBlock__ :堆区 (copy后Block存放在堆区)
应用场景:做逻辑操作,一般多见于属性,@property (nonatomic,copy) void (^SelectPeoplesOK)(NSString *employees);或者是执行[block copy]操作,然后赋值给另一个otherblock,那么使用otherblock的时候,就要注意循环引用了
Person *person = [[Person alloc] init];
__weak typeof(self) weakSelf = self;
person.personNameBlock = ^(NSString *personName) {
weakSelf.name = personName;
};
[person blockTest];
有的小伙伴可能觉得还需要在block里面执行一下__strong __typeof(weakSelf)strongSelf = weakSelf; 其实这个例子不要,下面讨论
2.如何定位Block类型
对于我自己,我有两个办法,一是打断点在block上面,然后看后台数据。或者是NSLog输出block。只有定位对了block类型,才能决定是否要考虑循环引用问题,不然就一直无脑用了。直接贴代码,贴图了。。。
#import typedef void(^PersonNameBlock)(NSString *personName);
@interface Person : NSObject
@property (nonatomic,copy) PersonNameBlock personNameBlock;
- (void)personWithName:(PersonNameBlock)personBlock;
- (void)blockTest;
- (void)test;
@end
#import "Person.h"
@implementation Person
- (void)personWithName:(PersonNameBlock)personBlock
{
NSLog(@"personBlock : %@",personBlock);
if (personBlock)
{
personBlock(@"张三");
}
}
- (void)blockTest
{
NSLog(@"personBlock : %@",self.personNameBlock);
if (self.personNameBlock)
{
self.personNameBlock(@"张三");
}
}
- (void)test
{
NSLog(@"xxxx");
}
@end
Person *person = [[Person alloc] init];
[person personWithName:^(NSString *personName) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:personName delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}];
![](https://img.haomeiwen.com/i6954301/466690f4349d8424.png)
3.png
![](https://img.haomeiwen.com/i6954301/5582e3e207cc6ce3.png)
4.png
![](https://img.haomeiwen.com/i6954301/d4dfe48d80852e12.png)
5.png
![](https://img.haomeiwen.com/i6954301/76cdce55cfacff41.png)
6.png
3.什么时候block里面需要使用__strong typeof(weakSelf)strongSelf = weakSelf
其实答案简单,就是防止在执行block的时候局部变量weakSelf已经被释放了,weakSelf ==nil,那么执行 weakSelf.属性 的时候,就有问题了。 如果一直都写,大部分情况下是没有问题的,但是要是深究下去,具体情况具体分析,那么部分情况是不需要写的。像上面的例子,就不需要写strongSelf。现在来一个必须要写strongSelf的demo吧。。。
Person *person = [[Person alloc] init];
__weak Person * weakPerson = person;
person.personNameBlock = ^(NSString *personName) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakPerson test];
});
};
[person blockTest];
直接运行这段代码会发现[weakPerson test];并没有执行,打印一下会发现,weakPerson 已经是 Nil 了,这是由于当我们的 viewDidLoad 方法运行结束,由于是局部变量,无论是 person 和 weakPerson 都会被释放掉,那么这个时候在 Block 中就无法拿到正真的 person 内容了。
正确写法
- (void)viewDidLoad {
[super viewDidLoad];
Person *person = [[Person alloc] init];
__weak Person * weakPerson = person;
person.personNameBlock = ^(NSString *personName) {
__strong Person * strongPerson = weakPerson;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[strongPerson test];
});
};
[person blockTest];
}
4.自我总结
在使用block的时候,首先还是先确定block类型,然后看看自己的block里面有没有用到“外部变量”,然后如果不确定是否需要block里写
__strong XXX *xxx,打个断点,跑一下代码,看看__weak XXX *xxx会不会被释放掉,久而久之,也就熟知自己经常写的block会不会有问题了,个人见解,哪里写的不对的,欢迎各位大佬指正批评,谢谢~~
网友评论