block循环引用

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,不用担心循环引用
block循环引用_第1张图片
A794F4179CAD08FDA8FF8D01ACC3B9BA.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];
}];
block循环引用_第2张图片

3.png

block循环引用_第3张图片

4.png

block循环引用_第4张图片

5.png

block循环引用_第5张图片

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会不会有问题了,个人见解,哪里写的不对的,欢迎各位大佬指正批评,谢谢~~

你可能感兴趣的:(block循环引用)