block的weak-strong dance

使用block,出现的主要问题就是循环引用,进而导致内存泄露。
而解决block的循环引用,有两种方法,一种是是将block内部引用的外部对象用weak修饰,另外一种方法是将指针手动置为nil。

然而使用weak修饰外部变量的方法在某些情况下,一旦block内部由于变量由weak修饰导致其内存被释放,在block内部再次使用该对象时由于其已经为nil,所以无法正常调用。这个时候就可以使用weak-strong dance这种方法来避免block内部变量被置为nil。

一 weak-strong dance简介

如下所示,由于控制器对demoBlock有强引用,demoBlock对self即控制器也有强引用, 即出现了循环引用,故在block前将self用weak修饰。但是为了避免在block内部weakSelf因为某种情况被释放后无法使用,故在block内部创建了强指针指向的strongSelf来代替弱指针指向的weakSelf,因为其为局部变量,所以block内码执行完即销毁,但是在代码块执行完是不会轻易销毁的。

__weak typeof(self) weakSelf = self;
    self.demoBlock = ^{
        ViewController *strongSelf = weakSelf;
        NSLog(@"%@",strongSelf.view);
        [NSThread sleepForTimeInterval:10.0];
        NSLog(@"%@",strongSelf.view);
    };

二 实验验证

实验方法参考了如下博客中的实验:原始实验链接

实验目的:

1.实现用weak修饰外部变量,但是该变量在block内部因为某种原因而被销毁,导致无法继续使用该变量。
2.使用weak-strong dance来解决上述变量在block内部被销毁而无法继续使用的问题。

实验思路:

创建2个线程-主线程和一个子线程,主线程在for循环到500时,指针置空,外部引用为0;而该子线程则for循环1000次输出

可能结果:

假如在主线程for循环到500时,block停止执行,即说明该外部变量在block内部被销毁,无法继续使用。
假如在主线程for循环到500时,block仍然成功执行到1000,即说明该外部变量未被销毁,可以正常使用。

实验过程:

没有weak-strong dance下运行代码:

实验类JWPoint中的代码
#import <Foundation/Foundation.h>

typedef void(^SuccBlock)(int data);
@interface JWPoint : NSObject

- (void)networkDataBack;
@end
#import "JWPoint.h"

@implementation JWPoint
- (void)networkDataBack{
    __weak __typeof(self)weakSelf = self;
    SuccBlock block = ^(int data){
        for (int i = 0; i < 1000; i ++) {
            [weakSelf go:i];
        }
    };
    block(11);
}

- (void)go:(int)number{
    NSLog(@"BLOCK GO %d",number);
}
@end
相关的调用代码
- (void)viewDidLoad {
    [super viewDidLoad];
    [self blockTest];
 }

-(void)blockTest{
    __block JWPoint *point = [JWPoint new];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        [point networkDataBack];
    });
    for (int i = 0; i < 501; i ++) {
        NSLog(@"Point will die");
        if (i == 500) {
            point = nil;
            NSLog(@"NO REFERENCE");
        }
    }
    NSLog(@"OVER");
}
实验的结果

如下图所示,子线程执行到307次时,主线程的500次循环执行完毕,随后子线程也不再执行。验证了单纯使用weak来修饰block内部引用的外部变量可以解决循环引用的问题,但是无法避免block内部该变量由于是弱指针指向,一旦被销毁无法继续使用的问题。
这里写图片描述

使用weak-strong dance下的代码:

在block内部,用强指针指向的strongSelf代替弱指针指向的weakSelf。

- (void)networkDataBack{
    __weak __typeof(self)weakSelf = self;
    SuccBlock block = ^(int data){
        JWPoint *strongSelf = weakSelf;
        for (int i = 0; i < 1000; i ++) {
            [strongSelf go:i];
        }
    };
    block(11);
}
实验结果如下:

当主线程500次循环执行完毕,子线程继续执行,一直执行完1000次循环。说明使用weak-strong dance可以避免block内部弱指针指向的外部变量被销毁的问题。使用这种方法可以完美解决block的循环引用问题。

这里写图片描述

三 AFNetworking以及ReactiveCocoa中使用weak-strong dance

AFNetworking中使用weak-strong dance

仅在下边这一个地方使用
下边代码出自AFNetworkReachabilityManager.h这个类中

__weak __typeof(self)weakSelf = self;
    AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
        __strong __typeof(weakSelf)strongSelf = weakSelf;

        strongSelf.networkReachabilityStatus = status;
        if (strongSelf.networkReachabilityStatusBlock) {
            strongSelf.networkReachabilityStatusBlock(status);
        }

    };

ReactiveCocoa中使用weak-strong dance

以下代码出自”NSControl+RACTextSignalSupport.h“类中,分别使用了weakify和strongify在block外部和内部修饰变量。

- (RACSignal *)rac_textSignal {
    @weakify(self);
    return [[[[RACSignal
        createSignal:^(id<RACSubscriber> subscriber) {
            @strongify(self);
            id observer = [NSNotificationCenter.defaultCenter addObserverForName:NSControlTextDidChangeNotification object:self queue:nil usingBlock:^(NSNotification *note) {
                [subscriber sendNext:note.object];
            }];

            return [RACDisposable disposableWithBlock:^{
                [NSNotificationCenter.defaultCenter removeObserver:observer];
            }];
        }]
        map:^(NSControl *control) {
            return [control.stringValue copy];
        }]
        startWith:[self.stringValue copy]]
        setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)];
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值