首先我们来讨论一下Viewcontrol无法释放的问题
最近在项目运行时发现这个内存消耗不是一般的高
这里内存较高也是有原因的,因为我这里展示了一张高度有两个屏幕高度的UIImageView。
好的吧,图片比较大内存消耗高一点我也就忍了,可是我现在的问题是,在我完全退出当前显示图片的ViewControl时,竟然不走
- (void)dealloc
- {
- ////
- }
这下问题就大了,我都退出了这个界面,怎么内存消耗还是不降啊?这样下去,我要是反复的点进这个界面,那这个内存消耗还得了。(当时注意到,在我反复进入这个页面的时候内存消耗不会再升高,还是保持在180的样子,可是没有考虑那么多,只怪自己菜)
归根结底,是因为当前控制器被某个对象强引用了,控制器的引用计数不为0,系统无法帮你释放这部分内存
以下是我找到的原因和解决方案:
1、控制器中NSTimer没有被销毁
当viewController中存在NSTimer时,需要特别注意,当调用
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES]
时,因为 target:self ,也就是引用了当前viewController,导致控制器的引用计数加1,如果没有将这个NSTimer 销毁,它将一直保留该viewController,无法释放,也就不会调用dealloc方法。所以,需要在viewWillDisappear之前需要把控制器用到的NSTimer销毁。
[timer invalidate]; // 销毁timer
timer = nil; // 置nil
2、viewController中的代理不是weak属性
例如@property (nonatomic, weak/assign) id delegate;代理要使用弱引用,因为自定义控件是加载在视图控制器中的,视图控制器view对自定义控件是强引用,如果代理属性设置为strong,则意味着delegate对视图控制器也进行了强引用,会造成循环引用。导致控制器无法被释放,最终导致内存泄漏。(善用XXX.delegate = self)
3、viewController中block的循环引用
在ARC下,block会把它里面的所有对象强引用,包括当前控制器self,因此有可能会出现循环引用的问题。比如viewController中有个block属性,在block中又强引用了self或者其他成员变量,那么这个viewController与自己的block属性就形成循环引用,导致viewController无法释放。
错误
self.tableView.mj_header= [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[self pullDownAction];
}];
//由于self是__strong修饰,在 ARC 下,当编译器自动将代码中的 block 从栈拷贝到堆时,block 会强引用和持有self,而self恰好也强引用和持有了 block,就造成了传说中的循环引用。
正确
A、typeof(self) __weak weakSelf = self;
B、id __weak weakSelf = self;
self.tableView.mj_header= [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[weakSelf pullDownAction];
}];
还有一个列子:
@interface MyObject : NSObject
{
myBlock blk;
id _obj;
}
@end
@implementation MyObject
- (id)init
{
self = [super init];
blk = ^{ NSLog(@"_obj = %@", _obj); };
return self;
}
...
...
@end
上面的例子中,虽然没有直接使用 self,却也存在循环引用的问题。因为对于编译器来说,_obj就相当于self->_obj,所以上面的代码就会变成
blk = ^{ NSLog(@"_obj = %@", self->_obj); };
另外,block 要用 copy修饰而且还有防止析构
4、当前类中的变量在其他类中使用
这个问题是需要慢慢检查的,当你要释放的页面中有变量在其他没有释放的页面中任然作用。那么,当前的页面就无法释放,原因也是就是引用计数的问题(我想可以考虑值传递)
5、我根据上面的方法也检查了我的代码,然并卵
好吧,我的问题其实很简单,应该是我刚开始就发现的,只是没有考虑。
绕了一大圈我们来看看我们界面内存消耗的源头,没错UIimageView。
我的项目里使用的是:
NSString *path = [NSString stringWithFormat:@"%@/%@.png",RecordTempRoute,self.midiFile];
UIImageView * imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:path]];
这就是问题的根源,[UIImage imageNamed:@”“]分配的图像系统会放到cache里面。而关于cache管理的规则就没有明确的介绍。由此看来[UIImage imageNamed:]只适合与UI界面中小的贴图的读取,而一些比较大的资源文件应该尽量避免使用这个接口。
[UIImage imageWithContentsOfFile]
我这里的解决方法是使用NSData
NSString *path = [NSString stringWithFormat:@"%@/%@.png",RecordTempRoute,self.midiFile];
NSData *data=[NSData dataWithContentsOfFile:path];
UIImageView * imgView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:data]];
这样在页面释放时,NSData就会被释放,理所当然这回走到了
dealloc这里。
哈哈 这就是我的解救过程,供自己记录。
附加:
释放自定义的UIView
[view removeFromSuperview];
view = nil;
这是我今天(2016.11.15)在项目中无法找到UIView中无法释放的元素时,自己手动将UIView置为了nil,也达到了效果。(无可奈何之选)