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

IOS6.0以后APP对内存警告的处理

2013-03-22 08:45 281 查看
官方文档:https://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html

iOS的版本号已经到了6.0.1了, 这篇文章说60%的iPhone用户已经升级到了iOS6了。那我们的应用有没有做好相关的准备工作呢。

今天在调试代码的时候,用模拟器模拟内存警告,结果发现自己的ViewController竟然不响应viewDidUnload函数,尼玛,这可是个大问题。

查询文档,iOS升级到6.0以后,不再支持viewDidUnload了。官方文档的解释是系统会自动控制大的View所占用的内存,其他小的View所占用的内存是极其微小的,不值得为了省内存而去清理然后在重新创建。如果你需要在内存警告的时候释放业务数据或者做些其他的特定处理,你可以实现 didReceiveMemoryWarning 这个函数。苹果官方文档请猛击

总结下:iOS6 之前:

viewDidUnload 和 didReceiveMemoryWarning 都会被调用。

iOS6 及其后面的版本。

viewDidUnload 不会被调用 didReceiveMemoryWarning 依然被调用。系统会自动处理view相关的内存,我们不用担心。

苹果官方给出的解释了相关的方案总是看起来十分美好的,但是现实往往是残酷。

1.我们的工程是ARC的。

2.我们会在viewController里面持有大量子view的成员变量(strong)

3.我们实现了大量的viewDidUnload函数来释放 (2)里面持有的那个子view。

让我们看看我们的代码到了iOS6以后会发生什么事情。因为所有的子view都是strong持有的,这样会导致,及时系统内存警告导致了view的回收,他们也不会被真正的释放。于是乎,我们的程序可能就在后台被系统频繁的杀死。

- (void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

if (!self.view.window) {

self.view = nil;

}

}

那么有了上面的代码,事情结束了吗?没有。下面说下这个方案的问题。(一个例子)

一个App 有 3个 tab,A 、B、C。(都从viewController继承,并且都实现了didReceiveMemoryWarning)。当程序启动的时候,默认显示tab A,这个时候,A 的 viewDidload被调用,并且加载数据显示给用户。然后我们切换到 tab B,B会重复A的加载过程。
这时候系统产生了一个内存警告,A、B、C 3个对象都会受到警告。

A对象:因为它已经不在当前UI显示了,所以满足[self.view window] == nil,相关view被释放。
B对象:正在显示,所有didReceiveMemoryWarning什么也不会干。
C对象:最悲惨,从来没有显示过,viewDidload从来没调用过,也没有显示过。然后有个self.view .这句的调用会导致一个结果,就是C对象的viewDidload会被调用一次,于是他的逻辑就是释放前先创建一次,然后再把自己释放,是不是很悲剧。(所以apple给的方案也不一定完美靠谱)

到这里故事也讲了,最后说说,在iOS6 及其以后,我们应该怎么处理这个问题。

1.不要把 subView 当成成员变量来持有。使用tag来操作。(其实不管在哪个版本最后都这么做)
2.不需要实现viewDidUnload,由系统自己来控制相关的内存释放
3.在需要的时候实现didReceiveMemoryWarning来释放一些业务数据减少内存的占用,不要操作UIView。

解决办法:(把代码改成:)

- (void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

if ([self isViewLoaded] && !self.view.window) {

self.view = nil;//
目的是再次进入时能够重新加载调用viewDidLoad函数。

}

}

也许有错误

这篇文章是这样解释的: http://www.cnblogs.com/yingkong1987/archive/2012/11/21.html 1、

iOS3-iOS6.0以前版本收到内存警告:

调用didReceiveMemoryWarning内调用super的didReceiveMemoryWarning会将controller的view进行释放。所以我们不能将controller的view再次释放。

处理方法:

-(void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];//如没有显示在window上,会自动将self.view释放。

// ios6.0以前,不用在此做处理,self.view释放之后,会调用下面的viewDidUnload函数,在viewDidUnload函数中做处理就可以了。

}

-(void)viewDidUnload

{

// Release any retained subviews of the main view.不包含self.view

[super viewDidUnload]; //处理一些内存和资源问题。

}

2、

iOS6.0及以上版本的内存警告:

调用didReceiveMemoryWarning内调用super的didReceiveMemoryWarning调只是释放controller的resouse,不会释放view

处理方法:

-(void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];//即使没有显示在window上,也不会自动的将self.view释放。

// Add code to clean up any of your own resources that are no longer necessary.

// 此处做兼容处理需要加上ios6.0的宏开关,保证是在6.0下使用的,6.0以前屏蔽以下代码,否则会在下面使用self.view时自动加载viewDidLoad

if ([self.view window] == nil)// 是否是正在使用的视图

{

// Add code to preserve data stored in the views that might be

// needed later.

// Add code to clean up other strong references to the view in

// the view hierarchy.

self.view = nil;// 目的是再次进入时能够重新加载调用viewDidLoad函数。

}

}

但我测试却发现ios6.0在调用上面的代码时就算不判断版本,也一样会调用viewDidLoad,只有加一个[self isViewLoaded]判断才不会调用viewDidLoad。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: