iOS 笔记之 Block


介绍一下OC中,ARC模式,使用block引起循环引用的问题


先写结论:(直接贴下面的代码,结论在此代码中)

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor yellowColor];
    
    NSLog(@"address:%p",self);
    __weak typeof(self) weakSelf = self;
    
    <span style="color:#ff0000;">/* 
     *(一)
     *
     * 使用 Block 的对象 作为:
     * 成员变量
     * 结论:作为成员变量的时候,self参与持有,所以可能形成(self->blockObj->block->self)三者的循环引用
     * 解决方式:
     *          对 self 和 成员变量使用:__weak
     */
    /*</span>
    self.testObj = [[TestBlock alloc] init];
    [self.testObj testCallWithBlock:^(id result) {
        
        // (1) leak [self & TestBlock]
        //id obj = self.testObj;
        //NSLog(@"address_block:%p",obj);
        
        // (2) leak [self & TestBlock]
        //id obj = self;
        //NSLog(@"address_block:%p",obj);
        
        // (3) unLeak
        //NSLog(@"testCallWithBlock_end");
        
        // (4) unLeak
        //id obj = weakSelf;
        //NSLog(@"address_block:%p",obj);
        
        // (5) unLeak
        //id obj = weakSelf.testObj;
        //NSLog(@"address_block:%p",obj);
        
    }];
     */
    
    <span style="color:#ff0000;">/*
     *(二)
     *
     * 使用 Block 的对象 作为:
     * 局部变量
     * 结论:作为局部变量的时候,self不参与持有,所以self不会形成循环引用,但是,
     *      局部变量可能引用自己,形成循环引用(下面情形(1))。
     * 解决方式:
     *      对此局部变量使用 __weak
     */</span>
    TestBlock *tempTestObj = [[TestBlock alloc] init];
    __weak typeof(tempTestObj) weakBlockObj = tempTestObj;
    [tempTestObj testCallWithBlock:^(id result) {
        
        // (1) leak [TestBlock]
        //id obj = tempTestObj;
        //NSLog(@"address_block:%p",obj);
        
        // (2) unLeak
        id obj = weakBlockObj;
        NSLog(@"address_block:%p",obj);
        
        // (3) unLeak
        //id obj = self;
        //NSLog(@"address_block:%p",obj);
        
        // (4) unLeak
        //NSLog(@"testCallWithBlock_end");
        
        // (5) unLeak
        //id obj = weakSelf;
        //NSLog(@"address_block:%p",obj);
        
    }];
}


完整测试代码如下:

//
//  ViewController.m
//  BlockTest
//
//  Created by liuchaoran on 15/3/19.
//  Copyright (c) 2015年 liuchaoran. All rights reserved.
//

/*
 * 测试思路:
 * 创建另一个 TestViewController,展示这个界面 2 秒,再 dismiss 此界面。
 * (如果单纯这样一个空的 TestViewController ,在 dismiss 的时候,毫无疑问是要被释放的,不解释)。
 * 在 TestViewController 创建成功后马上调用 TestBlock ,TestBlock 3 秒返回。
 * 也就是说,TestBlock 在 TestViewController 消失之后才返回结果,这时候看一下 TestBlock 和 TestViewController 是否被释放。
 */

#import "ViewController.h"
#import "TestViewController.h"

@interface ViewController ()
@property(nonatomic,strong)TestViewController *testVc;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.frame = CGRectMake(50, 100, 200, 50);
    [button setTitle:@"推出下个页面" forState:UIControlStateNormal];
    [button setBackgroundColor:[UIColor blackColor]];
    [button addTarget:self action:@selector(pushNext) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    
    TestViewController *tv = [[TestViewController alloc] init];
    self.testVc = tv;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)pushNext
{
    NSLog(@"pushNext_start");
    [self presentViewController:self.testVc animated:NO completion:^{
        NSLog(@"presentViewController_present_complete");
    }];
    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(popNext) userInfo:nil repeats:NO];
}

-(void)popNext
{
    NSLog(@"popNext_start");
    [self.testVc dismissViewControllerAnimated:NO completion:^{
        NSLog(@"presentViewController_dismiss_complete");
    }];
    self.testVc = nil;
}

@end

//
//  TestViewController.m
//  BlockTest
//
//  Created by liuchaoran on 15/3/19.
//  Copyright (c) 2015年 liuchaoran. All rights reserved.
//

#import "TestViewController.h"
#import "TestBlock.h"

@interface TestViewController ()
@property(nonatomic,strong)TestBlock *testObj;
@end

@implementation TestViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor yellowColor];
    
    NSLog(@"address:%p",self);
    __weak typeof(self) weakSelf = self;
    
    /* 
     *(一)
     *
     * 使用 Block 的对象 作为:
     * 成员变量
     * 结论:作为成员变量的时候,self参与持有,所以可能形成(self->blockObj->block->self)三者的循环引用
     * 解决方式:
     *          对 self 和 成员变量使用:__weak
     */
    /*
    self.testObj = [[TestBlock alloc] init];
    [self.testObj testCallWithBlock:^(id result) {
        
        // (1) leak [self & TestBlock]
        //id obj = self.testObj;
        //NSLog(@"address_block:%p",obj);
        
        // (2) leak [self & TestBlock]
        //id obj = self;
        //NSLog(@"address_block:%p",obj);
        
        // (3) unLeak
        //NSLog(@"testCallWithBlock_end");
        
        // (4) unLeak
        //id obj = weakSelf;
        //NSLog(@"address_block:%p",obj);
        
        // (5) unLeak
        //id obj = weakSelf.testObj;
        //NSLog(@"address_block:%p",obj);
        
    }];
     */
    
    /*
     *(二)
     *
     * 使用 Block 的对象 作为:
     * 局部变量
     * 结论:作为局部变量的时候,self不参与持有,所以self不会形成循环引用,但是,
     *      局部变量可能引用自己,形成循环引用(下面情形(1))。
     * 解决方式:
     *      对此局部变量使用 __weak
     */
    TestBlock *tempTestObj = [[TestBlock alloc] init];
    __weak typeof(tempTestObj) weakBlockObj = tempTestObj;
    [tempTestObj testCallWithBlock:^(id result) {
        
        // (1) leak [TestBlock]
        //id obj = tempTestObj;
        //NSLog(@"address_block:%p",obj);
        
        // (2) unLeak
        id obj = weakBlockObj;
        NSLog(@"address_block:%p",obj);
        
        // (3) unLeak
        //id obj = self;
        //NSLog(@"address_block:%p",obj);
        
        // (4) unLeak
        //NSLog(@"testCallWithBlock_end");
        
        // (5) unLeak
        //id obj = weakSelf;
        //NSLog(@"address_block:%p",obj);
        
    }];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)dealloc
{
    NSLog(@"TestViewController_dealloc");
}

@end

//
//  TestBlock.h
//  BlockTest
//
//  Created by liuchaoran on 15/3/19.
//  Copyright (c) 2015年 liuchaoran. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef void (^myBlock)(id result);

@interface TestBlock : NSObject

@property(nonatomic,strong)myBlock completionBlock;

-(void)testCallWithBlock:(myBlock)returnBlock;

@end

//
//  TestBlock.m
//  BlockTest
//
//  Created by liuchaoran on 15/3/19.
//  Copyright (c) 2015年 liuchaoran. All rights reserved.
//

#import "TestBlock.h"


@implementation TestBlock

-(void)dealloc
{
    NSLog(@"TestBlock_dealloc");
}

-(void)testCallWithBlock:(myBlock)returnBlock
{
    self.completionBlock = [returnBlock copy];
    [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(end) userInfo:nil repeats:NO];
}

-(void)end
{
    if (self.completionBlock) {
        self.completionBlock(@"shit");
    }
}

@end



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值