介绍一下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