ARC下的内存泄露
iOS提供了ARC功能,很大程度上简化了内存管理的代码。
但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露。
下面列举两种ARC导致内存泄露的情况。
1,循环参照
A有个属性参照B,B有个属性参照A,如果都是strong参照的话,两个对象都无法释放。
这种问题常发生于把delegate声明为strong属性了。
例,
@interface SampleViewController
@property (nonatomic, strong) SampleClass *sampleClass;
@end
@interface SampleClass
@property (nonatomic, strong) SampleViewController *delegate;
@end
上例中,解决办法是把SampleClass 的delegate属性的strong改为assing即可。
2,死循环
如果某个ViewController中有无限循环,也会导致即使ViewController对应的view关掉了,ViewController也不能被释放。
这种问题常发生于animation处理。
例,
比如,
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
tansition.repeatCount = HUGE_VALL;
[self.view.layer addAnimation:transition forKey:"myAnimation"];
上例中,animation重复次数设成HUGE_VALL,一个很大的数值,基本上等于无限循环了。
解决办法是,在ViewController关掉的时候,停止这个animation。
-(void)viewWillDisappear:(BOOL)animated {
[self.view.layer removeAllAnimations];
}
内存泄露的情况当然不止以上两种。
即使用了ARC,我们也要深刻理解iOS的内存管理机制,这样才能有效避免内存泄露。
arc的程序出现内存泄露怎办
实例一:
用arc和非arc混编,非arc的类在arc里实例化并且使用,在arc里居然出现内存泄露,而且应为是arc,所以无法使用release,autorelease和dealloc去管理内存。正常情况下应该是不会出现这种情况的,某一个类若是ARC,则在这个类里面都应该遵循ARC的用法,而无需关心用到的类是否是ARC的,同样,在非ARC类里面,就需要遵循内存管理原则。
用ARC,只是编译器帮你管理了何时去release,retain,不用ARC就需要你自己去管理,说到底只是谁去管理的问题,所以你再好好看看,可能问题与ARC无关。
如果实在找不到问题,建议你找到泄露的那个对象,将其赋值为nil,因为ARC里面,一旦对象没有指针指向,就会马上被释放。
实例二:
最近在学objective-c,我发现创建项目时如果使用了ARC,非常容易内存泄露,经常某个对象已经被释放掉了我还在使用,由于不太了解这个机制,现在我举出两个例子,请经验者帮我分析一下。
例子一:一开始,在AppDelegate.m的那个开始方法中时这样写的:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; // UITabBarController tabBarController = [[UITabBarController alloc] init]; [tabBarController setViewControllers:[self showConnectViewOnWindow]]; [tabBarController setDelegate:self]; // [[self window] addSubview: [tabBarController view]]; [self.window makeKeyAndVisible]; return YES;} |
然后,我还做了其他的工作:tabBarController中有tabBarItem,点击会调用一个方法
但是每次一点击,就会报unrecognized selector send to instance的错误,
后来上网一查,说是要把tabBarController定义成全局变量,不然这个方法一结束,tabBarController就被释放掉了,这样点击产生时间的对象都没了,于是我把它定义成全局变量,确实可以了,但我的疑问是,为什么方法一结束他就会释放掉吗,[[self window] addSubview: [tabBarController view]];我这一句不是已经在self window里引用它了吗,他怎么还会被释放,我觉得java和C#里面这种情况是不会释放掉了。
|
1
2
3
4
5
6
7
8
|
例子二:在viewdidload方法里面: [self.navigationItem setTitle:Title]; leftButton = [[UIBarButtonItem alloc] initWithTitle:Cancel style:UIBarButtonItemStyleBordered target:self action:@selector(CancleButtonClicked)]; self.navigationItem.leftBarButtonItem = leftButton; |
这里我给屏幕上方那个导航条加了一个左边的按钮,然后点击这个按钮后会用方法CancleButtonClicked来响应,但是我运行起来一点击,还是报unrecognized selector send to instances错误了,这里又是哪个对象释放了,leftButton吗?但是self.navigationItem.leftBarButtonItem = leftButton已经引用了啊。
解决方法:
例子一[[self window] addSubview: [tabBarController view]];
你只引用了tabBarController的view,没有引用tabBarController
例子二,不知道什么原因,看看有没有拼写错误吧。
另外,我感觉局部变量的内存一般只在它的生命周期内有效。出了它所定义的区域,即使不释放,也最好不要用了。
自从使用了ARC,代码写起来方便了很多,不需要retain,release,dealloc了,但是也隐藏了一些释放内存的问题。
self进行retain,这有时候有可能会产生一个引用环(两个或以上的对象之间直接或间接地互相引用)并导致内存泄露。解决的方法是:当需要在Block中访问实例变量的时候,创建一个指向self的指针,并对其使用__block修饰符,这样self不会被自动retain:BLock的内存泄露
在我们代码中关于block的使用可以说随处可见,第一次接触block的时候是关于UIView的块动画,那时觉得block的使用好神奇, 再后来分析总结为block其实就是一个c语言函数,只是我们可以在任意处调用此函数。有了这样的理解我开始经常使用block。在做项目以后发现使用 block竟然会引起内存泄露,于是开始自己调试研究block的内存管理问题。
普通的block使用(包括块动画)
这里有一个简单的block使用,在里面我们可以添加任何自己想进行的操作,大部分的使用也是如此
1 void (^Block)(int) = ^(int num){
2 //此处还可添加其他代码 ....
3 NSLog(@"int number is %d",num);
4 };
包括UIView的块动画也是如此使用,在这里我们定义了一个图像视图的位置及透明度的变化
1 [UIView animateWithDuration:2.0 animations:^(void){
2 smallImage.frame = CGRectMake(150, 80, 30, 30);
3 } completion:^(BOOL finished) {
4 smallImage.alpha = 0;
这些block操作中,我一直都认为block中的对象会在block使用后就被释放(但UIView的操作好像是这么做的)
block内存管理初现
直到我在项目中遇见这样一个情况:我设置有2个控制器first及second,其中second中包含一个block对象,而block的实 现是在first中实现(一般的block传值都是这么做)。而界面的推送是由first控制器push出second控制器。但当我second控制器 pop的时候,问题出现second控制器不走delloc方法,即pop后second控制器还存在没有被销毁(因为当时要做delloc中做一些操 作,才发现这个问题)!block示例代码如下:
first控制器中block的实现
1 SecondViewController *secondVC = [[SecondViewController alloc] init];
2 secondVC .block = ^(NSString *text){
3 self.text = secondVC.text;
4 };
这么一个简单的传值block使用,居然能引起second控制器无法释放,于是研究其原理,并网上搜索资料,得出一个结论:second控制 器在block中被持有一次才导致其无法释放。因为block本质上是一个函数,而编译器不知道你什么时候会调用block里面的值,所以为了确保编译器 内secondVC不会被释放,编译器会自动对其进行一次持有(在自身类中使用block方法操作自身的成员属性也会使自己的引用计算加1,造成无法释 放)。
其解决办法也简单 在外部添加一个弱引用对象指向需要在block中操作的对象,即__weak typeof(对象名) 别名= 对象名;
1 SecondViewController *secondVC = [[SecondViewController alloc] init];
2 __weak typeof(secondVC) second = secondVC;
3 __weak typeof(self) vc = self;
4 2 secondVC .block = ^(NSString *text){
5 3 vc.text = second.text;
6 4 };
这样就能够有效的防止block使用引起的内存泄露问题。
NSTimer使用问题
另外在还在项目中遇见一个关于NSTimer的使用问题。我们想到在控制器销毁时同时停止NSTimer并置为nil
1 -(void)dealloc {
2 [self.timer invalidate];
3 self.timer = nil;
4 NSLog(@"%@ dealloc", NSStringFromClass([self class]));
5 }
然而控制器被pop后并没有走此方法(又是内存泄露),由于之前出现了Block内存泄露的问题,我就想是不是因为这个_timer加载的时候对self进行了一次持有
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerUp:) userInfo:nil repeats:YES];
进行调试测验,果然是这里出了问题,因为其对控制器持有了一次。于是我想到既然这样那我干脆就在viewWillDisappear()中做个判 断,如果是pop控制器,我就先设置[self.timer invalidate]操作这样控制器就会走dealloc()方法。后来再网上找资料发现了一个更简单的解决办法,即同Block的内存管理一样使用弱 引用对象
1 __weak typeof(self) vc = self;
2 _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:vc selector:@selector(timerUp:) userInfo:nil repeats:YES];
这样的解决办法就要比我之前的要简单多了,唯一需要注意的就是此处vc的作用域!
ARC下的内存泄露的更多相关文章
- ARC模式下的内存泄露问题
ARC模式下的内存泄露问题 iOS提供的ARC 功能很大程度上简化了编程,让内存管理变得越来越简单,但是ARC并不是说不会发生内存泄露,使用不当照样会发生. 以下列举两种内存泄露情况: 死循环造成的内 ...
- ARC下的内存管理
1.ARC下单对象内存管理 局部变量释放对象随之被释放 int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = ...
- Linux Debugging (九) 一次生产环境下的“内存泄露”
一个偶然的机会,发现一个进程使用了超过14G的内存.这个进程是一个RPC server,只是作为中转,绝对不应该使用这么多内存的.即使并发量太多,存在内存中的数据太多,那么在并发减少的情况下,这个内存 ...
- vs下C++内存泄露检测
本文原链接: http://www.cnblogs.com/zouzf/p/4152279.html 参考文章: http://msdn.microsoft.com/zh-cn/library/x98 ...
- ARC 下处理内存暴涨的一个解决办法
有一种情况: ; i < ; i++) { NSString *s = @"ABC"; s = [s lowercaseString]; s = [s stringByApp ...
- Linux下检测内存泄露的工具 valgrind
参考:http://www.cnblogs.com/sunyubo/archive/2010/05/05/2282170.html 几乎是照抄参考过来的,只不过后面自己调试一下代码. 这里主要介绍Va ...
- 【转】iOS夯实:ARC时代的内存管理
iOS夯实:ARC时代的内存管理 什么是ARC Automatic Reference Counting (ARC) is a compiler feature that provides autom ...
- Block产生的内存泄露,以及解决方法
前言: 在ARC(自动引用技术)前,Objective-c都是手动来分配释放 释放 计数内存,其过程非常复杂. ARC技术推出后,貌似世界和平了很多,但是其实ARC并不等同于Java或者C#中的垃圾回 ...
- iOS夯实:ARC时代的内存管理
iOS夯实:ARC时代的内存管理 文章转自 ARC时代的内存管理 什么是ARC Automatic Reference Counting (ARC) is a compiler feature tha ...
随机推荐
- M - 昂贵的聘礼 - poj1062
Description 年轻的探险家来到了一个印第安部落里.在那里他和酋长的女儿相爱了,于是便向酋长去求亲.酋长要他用10000个金币作为聘礼才答应把女儿嫁给他.探险家拿不出这么多金币,便请求酋长降低 ...
- Appium移动自动化测试(四)--one demo(转)
Appium移动自动化测试(四)--one demo 2015-06-15 20:41 by 虫师, 40514 阅读, 34 评论, 收藏, 编辑 继续更新. ------------------- ...
- [Flexbox] Using order to rearrange flexbox children
Using the order property we alter the order in which flexbox children appear on the page, without ma ...
- 常用颜色大全---RGB值及中英文名称
■■■■■ #DC143C Crimson 深红/猩红 ■■■■■ #FFF0F5 LavenderBlush 淡紫红 ■■■■■ #DB7093 PaleVioletRed 弱紫罗兰红 ■■■■■ ...
- POJ 1228 Grandpa's Estate(凸包)
Grandpa's Estate Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 11289 Accepted: 3117 ...
- Linux 下Mysql自动备份脚本
backdb.sh 文件 #!/bin/bash USER="root" PASSWORD="888888" DATABASE="mydb" ...
- 动态代理 Proxy InvocationHandler
前奏 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常 ...
- ADO.NET基础
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.W ...
- 错误记录--关于foreach,集合已修改;可能无法执行枚举操作
集合已修改,可能无法执行枚举操作.今天在使用foreach遍历的时候出现了这样的错误.查了一下,这个是使用foreach的典型的错误问题问题.foreach在遍历取数据的过程中,枚举器只允许读,不允许 ...
- HTML简要内容
1. html基础 html是用来制作网页的标记语言,不需编译,直接由浏览器执行.大小写不敏感,推荐使用小写.html文件必须使用html或htm为文件名后缀. html主体结构: (1)DTD头: ...