您的位置:首页 > 移动开发 > IOS开发

iOS内存话题: performSelector 导致不立即 dealloc

2015-11-16 23:53 651 查看
这次的例子是

1. 基于 iphone 项目.

2. 在模拟器上面测试的.

3. 基于 ARC.

例子比较简单, A ViewController 启动 B ViewController.

主要代码在 B ViewController 里面.

@interface BViewController ()

@property (strong, nonatomic) NSMutableArray *tmpData;

@end

@implementation BViewController

- (void)dealloc
{
NSLog(@"---------------------------");
NSLog(@"MyViewController dealloc.");
NSLog(@"---------------------------");
}

- (void)viewDidLoad
{
[super viewDidLoad];

_tmpData = [NSMutableArray arrayWithObjects:@"mark.z", nil];

UIButton *cloneMeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[cloneMeBtn setTitle:@"close" forState:UIControlStateNormal];
cloneMeBtn.backgroundColor = [UIColor blueColor];
cloneMeBtn.frame = CGRectMake(130, 300, 80, 50);
[self.view addSubview:cloneMeBtn];
[cloneMeBtn addTarget:self action:@selector(close) forControlEvents:UIControlEventTouchUpInside];

[self performSelector:@selector(block1) withObject:nil afterDelay:3.0f];
}

- (void)block1
{
[UIView animateWithDuration:1 animations:^{
NSLog(@"tmpArray = %@", self.tmpData);
[self.tmpData addObject:@"hk"];
NSMutableArray *array = self.tmpData;
[array addObject:@"ju"];
} completion:^(BOOL finished) {

}];
}

- (void)block2:(id)sender
{
NSLog(@"sender = %@", sender);
MyViewController __weak *weakSelf = sender;
[UIView animateWithDuration:1 animations:^{
NSLog(@"tmpArray = %@", weakSelf.tmpData);
[weakSelf.tmpData addObject:@"hk"];
NSMutableArray *array = weakSelf.tmpData;
[array addObject:@"ju"];
} completion:^(BOOL finished) {
}];
}

- (void)block3
{
[UIView animateWithDuration:1 animations:^{
NSLog(@"tmpArray = %@", _tmpData);
[_tmpData addObject:@"hk"];
NSMutableArray *array = _tmpData;
[array addObject:@"ju"];
NSLog(@"tmpArray = %@", _tmpData);
} completion:^(BOOL finished) {
}];
}

- (void)close
{
[self dismissViewControllerAnimated:YES completion:^{

}];
}

@end


代码可以看出:

[self performSelector:@selector(block1) withObject:nil afterDelay:3.0f];

调用了 block1.

启动, 并点击 close 按钮, 关闭 B ViewController, 查看打印结果

3s 后打印如下

tmpArray = (
"mark.z"
)
---------------------------
MyViewController dealloc.
---------------------------


其实, 我想要的结果是, close 之后立即调用 dealloc, 但是现在需要 3s 后才调用.

试想一下, 如果来回切换并且达到一定次数, 是不是会内存泄露?!

不能忍!

大家可能会想, 是不是因为在 block1里面使用了 self, 造成无法立即释放.

那我们, 调用 block2

[self performSelector:@selector(block2:) withObject:nil afterDelay:3.0f];

看一下结果, 还是3s 后执行

sender = (null)
tmpArray = (null)
---------------------------
MyViewController dealloc.
---------------------------
看来不是, self 的问题.

block2 与 block1不同的就是, 使用了 weak 引用了当前 self(BViewcontroller).

这次, 至少告诉我们, self 已经是 null 了.

在调用一次 block3, 看看有没有新的发现.

[self performSelector:@selector(block3) withObject:nil afterDelay:3.0f];

还是3s 后得到结果

tmpArray = (
"mark.z"
)
tmpArray = (
"mark.z",
hk,
ju
)
---------------------------
MyViewController dealloc.
---------------------------


好吧, 醉了!

到底罪魁祸首是谁?

答案就是:

performSelector......

在调用

[self performSelector:@selector(block3) withObject:nil afterDelay:3.0f];

我才是对当前对象BViewcontroller有了 stong 的引用, 在 MRC 里面大家可以理解为 retain.

造成, 在 close 时, 无法立即释放自己(dealloc方法没有立即调用).

其实, apple 为我们提供了取消 performSelector 的方法.

+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;


修改 close 方法的代码

- (void)close
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self dismissViewControllerAnimated:YES completion:^{

}];
}


再次运行, 可以发现, dealloc 方法立即调用了.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: