在iOS中,不可避免的会用的强引用和弱引用,特别是在block块中使用,关键词也很直白就是weak和strong。因为OC中采用的引用计数的方式,当引用计数为0时该对象会被释放,不为0时会一直存在不被释放。使用strong就会将引用计数加1,当持有的对象被释放的时候,就会对应的减掉该引用数,而当使用weak时,不会修改引用计数。
用图片举一个很有趣的例子
强引用
这条狗被4个人(对象)强引用,只有当这四条绳子全部放开,也就是这四个人(引用对象)全都不存在了,这时候狗就可以跑了。
弱引用
狗被一个对象强引用,被绳子拴着,而其他三个都是弱引用,只是看着这条狗。当弱引用的对象走开了,并不会让狗跑掉,但是一旦那个强引用的人把对象释放掉,撒开绳子,狗会立马跑掉,即便是有多个弱引用的人看着。
强弱引用中碰到的问题
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给消除掉
专门讲tabview布局的小一点,点击之后,vc可以清晰的看到背景颜色的变化
但是当点击头部退出按钮的时候,发现退出之后,并没有调用dealloc的函数,也就是对象并没有被销毁,这样的话哪里存在了引用呢了。
字典强引用修改
字典在存入对象的时候,默认是用的强引用的方式,所以如果想修改这个,可以通过将对象专为NSValue的方式,通过使用NSValue的valueWithNonretainedObject
和nonretainedObjectValue
,从而去掉对象的强引用。
一、这里修改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]];
}
}
参考文章
- (再次理解strong与weak)强引用与弱引用
- iOS - 如何实现弱引用字典
- iOS如何在数组中使用弱引用(weak reference)
- viewController被POP后不调用dealloc的问题
- iOS NSNotificationCenter 使用姿势详解
版权属于:东哥笔记 - DongGe.me
本文链接:https://dongge.org/blog/648.html
自2017年12月26日起,『转载以及大段采集进行后续编辑』须注明本文标题和链接!否则禁止所有转载和采集行为!