二谈iOS强引用和弱引用

在iOS中,不可避免的会用的强引用和弱引用,特别是在block块中使用,关键词也很直白就是weak和strong。因为OC中采用的引用计数的方式,当引用计数为0时该对象会被释放,不为0时会一直存在不被释放。使用strong就会将引用计数加1,当持有的对象被释放的时候,就会对应的减掉该引用数,而当使用weak时,不会修改引用计数。

用图片举一个很有趣的例子

强引用

图片2.png

这条狗被4个人(对象)强引用,只有当这四条绳子全部放开,也就是这四个人(引用对象)全都不存在了,这时候狗就可以跑了。

弱引用

图片1.png

狗被一个对象强引用,被绳子拴着,而其他三个都是弱引用,只是看着这条狗。当弱引用的对象走开了,并不会让狗跑掉,但是一旦那个强引用的人把对象释放掉,撒开绳子,狗会立马跑掉,即便是有多个弱引用的人看着。

强弱引用中碰到的问题

block块中的强弱引用

强引用和弱引用在block块中的讨论在之前就谈过,所以直接给上链接地址:《强引用与弱引用以及在block块中的使用

字典和数组中的引用以及在cell中的引用

这个问题是在最近的开发项目中碰到的,以前没有注意,现在在测试的时候对应的ViewController在pop之后居然没有销毁掉,然后引发了其他问题,这时候才去排查了这个内存问题,现在写一个很简单的demo复现和说明下。

首先在cell中,将自己所在的VC传进来,这个VC的作用就是点击cell的时候,将VC.view的背景色修改下,下面就是简单的实现代码


@interface CustomTableViewCell()
@property (strong,nonatomic) UIViewController *VC;
@end

//通过字典将VC传入cell
-(void)initViewWithDic:(NSMutableDictionary*)dic
{
    [self.textLabel setText:[dic objectForKey:@"title"]];
    [self.detailTextLabel setText:[dic objectForKey:@"des"]];
    self.VC = [dic objectForKey:@"vc"];
}

//点击cell修改view的背景颜色
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    if (selected) {
        [self.VC.view setBackgroundColor:[UIColor redColor]];
    }
}

而在这个VC中呢,是这样将vc传入到字典,然后初始化cell的

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

///初始化数据
-(void)initData{
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    [dic setObject:self forKey:@"vc"];
    [dic setObject:@"标题" forKey:@"title"];
    [dic setObject:@"描述" forKey:@"des"];
    
    NSMutableDictionary *dic2 = [NSMutableDictionary dictionary];
    [dic2 setObject:self forKey:@"vc"];
    [dic2 setObject:@"ssssssssssss标题" forKey:@"title"];
    [dic2 setObject:@"ssssssssssss描述" forKey:@"des"];
    _dataArray = [NSMutableArray array];
    
    [_dataArray addObject:dic];
    [_dataArray addObject:dic2];
}

///布局cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *indentifier = @"test";
    CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:indentifier];
    if (!cell) {
        cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:indentifier];
    }
    [cell initViewWithDic:[_dataArray objectAtIndex:indexPath.row]];
    return cell;
}

这样布局之后,效果就是点击cell可以修改当前vc的背景色,当点击退出按钮的时候,将当前的vc给消除掉

Simulator Screen Shot - iPhone 6s - 2017-12-07 at 19.24.13.png

专门讲tabview布局的小一点,点击之后,vc可以清晰的看到背景颜色的变化

Simulator Screen Shot - iPhone 6s - 2017-12-07 at 19.24.18.png

但是当点击头部退出按钮的时候,发现退出之后,并没有调用dealloc的函数,也就是对象并没有被销毁,这样的话哪里存在了引用呢了。

字典强引用修改

字典在存入对象的时候,默认是用的强引用的方式,所以如果想修改这个,可以通过将对象专为NSValue的方式,通过使用NSValue的valueWithNonretainedObjectnonretainedObjectValue,从而去掉对象的强引用。

一、这里修改dic和dic2设置self为对象的函数

[dic setObject:self forKey:@"vc"];
[dic2 setObject:self forKey:@"vc"];

修改为

NSValue *value = [NSValue valueWithNonretainedObject:self];
[dic setObject:value forKey:@"vc"];
NSValue *value2 = [NSValue valueWithNonretainedObject:self];
[dic2 setObject:value2 forKey:@"vc"];

二、然后将cell中的取值函数也做对应的修改

self.VC = [dic objectForKey:@"vc"];

修改为

NSValue *value = [dic objectForKey:@"vc"];
self.VC = value.nonretainedObjectValue;

这样就解决掉了字典中的强引用。

cell中的强引用

再运行一次,发现依旧没有销毁对象,继续寻找,找到了cell这里,在赋值vc的时候,使用的是

@interface CustomTableViewCell()
@property (strong,nonatomic) UIViewController *VC;
@end

这样就造成了cell在强引用传过来的VC,而cell又在VC中,所以造成没有释放掉。

解决方案就是去掉这里的强引用,这里可以使用两种方案

一种是将strong修改为weak,去掉VC的强引用

@interface CustomTableViewCell()
@property (weak,nonatomic) UIViewController *VC;
@end

第二种是在用的时候使用局部变量,cell不强引用VC,使用字典存储

@interface CustomTableViewCell()
@property (strong,nonatomic) NSMutableDictionary *dict;
@end

在使用的地方使用局部变量

//通过字典将VC传入cell
-(void)initViewWithDic:(NSMutableDictionary*)dic
{
    _dict = [NSMutableDictionary dictionaryWithDictionary:dic];
    [self.textLabel setText:[_dict objectForKey:@"title"]];
    [self.detailTextLabel setText:[_dict objectForKey:@"des"]];
}

//点击cell修改view的背景颜色
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
    if (selected) {
        NSValue *value = [_dict objectForKey:@"vc"];
        UIViewController *VC = value.nonretainedObjectValue;
        [VC.view setBackgroundColor:[UIColor redColor]];
    }
}

参考文章

Last modification:December 7th, 2017 at 08:31 pm
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment