Block循环引用

block中循环引用问题

由于block会对block中的对象进行持有操作,而如果此时block中的对象又持有了该block,则会造成循环引用。如下:

@property(nonatomic, copy) void(^myBlock)();

@property(nonatomic, copy) NSString *blockString;

- (void)testBlock {

    self.myBlock = ^() {

        //其实注释中的代码,同样会造成循环引用

        NSString *localString = self.blockString;

        //NSString *localString = _blockString;

        //[self doSomething];

    };

}

注:以上调用注释掉的代码同样会造成循环引用,因为不管是通过self.blockString还是_blockString,或是函数调用[self doSomething],因为只要 block中用到了对象的属性或者函数,block就会持有该对象而不是该对象中的某个属性或者函数。

解决方法:

__weak typeof(self) weakSelf =self;

self.myBlock = ^() {

NSString *localString = weakSelf.blockString;

};

使用__weak修饰self,使其在block中不被持有,打破循环引用。

注意:

1;判断下block是否为空,避免crash。

2 当不在使用指向block的指针时,将其置空。

weak的缺陷

#import "TestViewController.h"

@interface TestViewController (

@property (nonatomic, copy) void (^testBolck)(void);

@end

@implementationTestViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    __weak typeof(self) weakSelf = self;

    self.testBolck= ^{

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            [weakSelf delay];

        });

    };

    self.testBolck();

     // Do any additional setup after loading the view.

}

- (void)delay {

    NSLog(@"delay");

}

- (void)dealloc{

    NSLog(@"delloc");

}

@end

这里会有两种情况:

若push进TestViewController,5s之内没有pop回去的话,TestViewController中block会执行打印出来。

若push进TestViewController,5s之内pop回去的话,TestViewController会立即执行dealloc,从而导致TestViewController中block打印出(null)。这种情况就是使用weakSelf的缺陷,可能会导致内存提前回收。

weakSelf和strongSelf

#import "TestViewController.h"

@interface TestViewController (

@property (nonatomic, copy) void (^testBolck)(void);

@end

@implementationTestViewController

- (void)viewDidLoad {

[super viewDidLoad];

 __weak typeof(self) weakSelf = self;

 self.testBolck= ^{

        __strongTestViewController*strongSelf = weakSelf;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

            [strongSelf delay];

        });

    };

 self.testBolck();

// Do any additional setup after loading the view.

}

- (void)delay {

    NSLog(@"delay");

}

- (void)dealloc{

    NSLog(@"delloc");

}

@end

这么做和直接用self有什么区别,为什么不会有循环引用:外部的weakSelf是为了打破环,从而使得没有循环引用,而内部的strongSelf仅仅是个局部变量,存在栈中,会在block执行结束后回收,不会再造成循环引用。

这么做和使用weakSelf有什么区别:唯一的区别就是多了一个strongSelf,而这里的strongSelf会使TestViewController的对象引用计数+1,使得TestViewController pop回去的时候,并不会执行dealloc,因为引用计数还不为0,strongSelf仍持有TestViewController,而在block执行完,局部的strongSelf才会回收,此时TestViewController dealloc。

注:此方法只能保证在block执行期间对象不被释放,如果对象在block执行执行之前已经被释放了,该方法也无效。

那些常用的容易引起循环引用的地方

1,我们很多时间使用MJRefresh这个第三方库集成刷新。如下

self.tableview.mj_header = [MJRefreshHeader headerWithRefreshingBlock:^{

        [self delay];

    }];

如果上面没有使用__weak声明,那么self持有self.tableview,self.tableview又强引用 mj_header,mj_header又强引用self,self就不会被释放,那么dealloc就不会被调用,就造成内存泄露。

2,我们经常用- (id)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue*)queue usingBlock:(void(^)(NSNotification*note))block;这个方法注册通知。

[[NSNotificationCenter defaultCenter] addObserverForName:@"notificationName" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {

        [self delay];

    }];

我们都会在dealloc函数中释放通知,如果上面没有使用__weak声明,那么通知中心持有self.observer,observer又强引用 usingBlock,usingBlock又强引用self,self就不会被释放,那么dealloc就不会被调用,就造成内存泄露。

3,AlertViewController继承自UIAlertController,为了测试是否释放。

AlertViewController *alert = [AlertViewController alertControllerWithTitle:@"提示" message:nil preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *yesAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

        NSLog(@"%@====%@",alert.title,alert.message);

    }];

    UIAlertAction *noAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];

    [alertaddAction:yesAction];

    [alertaddAction:noAction];

    [self presentViewController:alert animated:YES completion:nil];

我们会发现AlertViewController中dealloc并未执行,那么alert持有UIAlertAction,UIAlertAction又强引用 handler,handler又强引用alert,alert就不会被释放,那么dealloc就不会被调用,就造成内存泄露。可以改成这样__weak typeof(alert) weakAlart = alert。

还有一个问题,实在不知道什么原因,也会造成内存泄漏。

AlertViewController *alert = [AlertViewController alertControllerWithTitle:@"提示" message:nil preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *yesAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

//        NSLog(@"%@",alert.title);

//        [self delay];

    }];

    UIAlertAction *noAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];

    [alertaddAction:yesAction];

    [alertaddAction:noAction];

    [self presentViewController:alert animated:YES completion:nil];

yesAction中handler执行的那两行代码,任意放开一个都不会造成当前VC的内存泄漏,但是两行都放开,就会造成当前VC不能释放,造成内存泄漏。猜想,handler中引用alert,造成alert不能释放,导致self持有了alert,现在alert又持有self,导致alert和self都不能正常释放。

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