`
啸笑天
  • 浏览: 3436254 次
  • 性别: Icon_minigender_1
  • 来自: China
社区版块
存档分类
最新评论

ios6 处理内存警告

 
阅读更多

     iPhone下每个app可用的内存是被限制的,如果一个app使用的内存超过20M,则系统会向该app发送Memory Warning消息。收到此消息后,app必须正确处理,否则可能出错或者出现内存泄露。

     app收到Memory Warning后会调用:UIApplication::didReceiveMemoryWarning -> UIApplicationDelegate::applicationDidReceiveMemoryWarning,然后调用当前所有的viewController进行处理。因此处理的主要工作是在viewController

    当我们的程序在第一次收到内存不足警告时,应该释放一些不用的资源,以节省部分内存。否则,当内存不足情形依然存在,iOS再次向我们程序发出内存不足的警告时,我们的程序将会被iOS kill掉。

 

    iOSUIViewController 类给我们提供了处理内存不足的接口。在iOS 3.0 之前,当系统的内存不足时,UIViewControllerdidReceiveMemoryWarining 方法会被调用,我们可以在didReceiveMemoryWarining 方法里释放掉部分暂时不用的资源。

    iOS3.0 开始,UIViewController增加了viewDidUnload方法。该方法和viewDIdLoad相配对。当系统内存不足时,首先UIViewControllerdidReceiveMemoryWarining 方法会被调用,而didReceiveMemoryWarining 会判断当前ViewControllerview是否显示在window上,如果没有显示在window上,则didReceiveMemoryWarining 会自动将viewcontroller view以及其所有子view全部销毁,然后调用viewcontrollerviewdidunload方法。如果当前UIViewControllerview显示在window上,则不销毁该viewcontrollerview,当然,viewDidunload也不会被调用了。但是到了ios6.0之后,这里又有所变化,ios6.0内存警告的viewDidUnload 被屏蔽,即又回到了ios3.0的时期的内存管理方式。   


    iOS3-iOS5.0
以前版本收到内存警告:
调用didReceiveMemoryWarning内调用superdidReceiveMemoryWarning会将controllerview进行释放。所以我们不能将controllerview再次释放。
处理方法:
        

 -(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];
              
        }
 

 

    iOS6.0及以上版本的内存警告:
调用didReceiveMemoryWarning内调用superdidReceiveMemoryWarning只是释放controllerresouse,不会释放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时自动加载viewDidUnLoad

            if ([[UIDevice currentDevice].systemVersion floatValue] >= 6.0) {

             //需要注意的是self.isViewLoaded是必不可少的,其他方式访问视图会导致它加载 ,在WWDC视频也忽视这一点

             if (self.isViewLoaded && !self.view.window)// 是否是正在使用的视图
             {
                   // 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函数。
             }

           }
    }

 

但是似乎这么写相对于以前并不省事。最终我们找到一篇文章,文章中说其实并不值得回收这部分的内存,原因如下:

1. UIViewUIResponder的子类,而UIResponder有一个CALayer的成员变量,CALayer是具体用于将自己画到屏幕上的。

2. CALayer是一个bitmap图象的包装类,当UIView调用自身的drawRect时,CALayer才会创建这个bitmap图象类。

3. 具体占内存的其实是一个bitmap图象类,CALayer只占48bytes, UIView只占96bytes。而一个iPad的全屏UIViewbitmap类会占到12M的大小!

4.iOS6时,当系统发出MemoryWarning时,系统会自动回收bitmap类。但是不回收UIViewCALayer类。这样即回收了大部分内存,又能在需要bitmap类时,根据CALayer类重建。

    所以iOS6这么做的意思是:我们根本没有必要为了几十byte而费力回收内存。

 

--------------------------切糕分割线--------------

PS:

1、关于这个的官方文档:https://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/ViewLoadingandUnloading/ViewLoadingandUnloading.html

2、zon2012貌似都没有ios6的这个兼容(其实view是没问题的,关键是资源)

 

分享到:
评论
1 楼 啸笑天 2013-05-14  
移动设备终端的内存极为有限,应用程序必须做好low-memory处理工作,才能避免程序因内存使用过大而崩溃。

low-memory 处理思路
通常一个应用程序会包含多个view controllers,当从view跳转到另一个view时,之前的view只是不可见状态,并不会立即被清理掉,而是保存在内存中,以便下一次的快速显现。但是如果应用程序接收到系统发出的low-memory warning,我们就不得不把当前不可见状态下的views清理掉,腾出更多的可使用内存;当前可见的view controller也要合理释放掉一些缓存数据,图片资源和一些不是正在使用的资源,以避免应用程序崩溃。

思路是这样,具体的实施根据系统版本不同而略有差异,本文将详细说明一下iOS 5与iOS 6的low-memory处理。

iOS 5 的处理
在iOS 6 之前,如果应用程序接收到了low-memory警告,当前不可见的view controllers会接收到viewDidUnload消息(也可以理解为自动调用viewDidUnload方法),所以我们需要在 viewDidUnload 方法中释放掉所有 outlets ,以及可再次创建的资源。当前可见的view controller 通过didReceiveMemoryWarning 合理释放资源,具体见代码注释。

举一个简单的例子,有这样一个view controller:
@interface MyViewController : UIViewController { 
    NSArray *dataArray; 

@property (nonatomic, strong) IBOutlet UITableView *tableView; 
@end

对应的处理则为:
#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning {
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
   
    // Relinquish ownership any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
    // Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
    // For example: self.myOutlet = nil;
    self.tableView = nil;
    dataArray = nil;
   
    [super viewDidUnload];
}

iOS 6 的处理
iOS 6 废弃了viewDidUnload方法,这就意味着一切需要我们自己在didReceiveMemoryWarning中操作。
具体应该怎么做呢?

1.将 outlets 置为 weak
当view dealloc时,没有人握着任何一个指向subviews的强引用,那么subviews实例变量将会自动置空。
@property (nonatomic, weak) IBOutlet UITableView *tableView;

2.在didReceiveMemoryWarning中将缓存数据置空
#pragma mark -  
#pragma mark Memory management  
 
 
- (void)didReceiveMemoryWarning 

    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated.  
    dataArray = nil; 
}
不要忘记一点,每当tableview reload 的时候,需要判断一下 dataArray ,若为空则重新创建。

兼容iOS 5 与 iOS 6
好吧,重点来了,倘若希望程序兼容iOS 5 与 iOS 6怎么办呢? 这里有一个小技巧,我们需要对didReceiveMemoryWarning 做一些手脚:
#pragma mark -
#pragma mark Memory management

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
   
    if ([self isViewLoaded] && self.view.window == nil) {
        self.view = nil;
    }
   
    dataArray = nil;
}

判断一下view是否是window的一部分,如果不是,那么可以放心的将self.view 置为空,以换取更多可用内存。

这样会是什么现象呢?假如,从view controller A 跳转到 view controller B ,然后模拟low-memory警告,此时,view controller A 将会执行self.view = nil ; 当我们从 B 退回 A 时, A 会重新调用一次 viewDidLoad ,此时数据全部重新创建,简单兼容无压力~~

Note:
如果你好奇Apple为什么废弃viewDidUnload,可以看看Apple 的解释:
Apple deprecated viewDidUnload for a good reason. The memory savings from setting a few outlets to nil just weren’t worth it and added a lot of complexity for little benefit. For iOS 6+ apps, you can simply forget about view unloading and only implement didReceiveMemoryWarning if the view controller can let go of cached data that you can recreate on demand later.

相关推荐

    解析iOS内存不足时的警告以及处理过程

    内存警告 ios下每个app可用的内存是被限制的,如果一个app使用的内存超过了这个阀值,则系统会向该app发送Memory Warning消息。收到消息后,app必须尽可能多的释放一些不必要的内存,否则OS会关闭app。 几种内存警告...

    iOS调试工具AllYourMemoriesAreBelong2iOS.zip

    AllYourMemoriesAreBelong2iOS 是一个方便的调试工具,其集成在 iOS 项目中,让开发者在调试过程中可以通过按动 iOS 物理设备的音量键来模拟内存警告通知的产生。该库由 @开源中国真理部部长 使用 Objective-C 语言...

    ios开发记录

    //判断两个字符串是否相等,不能使用==,使用等号是判断两个对象是否是一个对象,也就是是否是一个内存地址。 //判断字符串的内容是否相同应该使用nsstring的isEqualToString:方法 //在低版本的时候,如果直接点击...

    iPhone应用程序开发指南.中文.pdf

    观察低内存警告 25 定制应用程序的行为 25 以景观模式启动 25 和其它应用程序进行通讯 26 实现定制的URL模式 27 显示应用程序的偏好设置 31 关闭屏幕锁定 31 国际化您的应用程序 32 性能和响应速度的调优 34 不要...

    PINCache:适用于iOS,tvOS和OS X的快速,无死锁的并行对象缓存

    在iOS上,当应用收到内存警告或进入后台时, PINMemoryCache将清除自身。 PINDiskCache存储的对象将PINDiskCache保留,直到您手动调整或通过设置字节或期限限制来自己调整缓存为止。 PINCache和PINDiskCache接受...

    iOS ARC 完全指南

    iOS ARC 完全指南OS5ARC完全指南 GuanGyi Inc http://www.gungyi.com ARC完全指南 最显著的变化就是增加了 动引用计数)。是新 编译器的特性,完全消除了手动内 存管理的烦琐。在你的项目中使用是非常简单的,所有的...

    疯狂java培训项目源码-DeepBeliefSDK:Jetpac的iOSDeepBelief图像识别框架的SDK

    处理代码经过高度优化,可以在现代移动设备的内存和处理限制内运行,并且可以在 iPhone 5S 上在 300 毫秒内分析图像。 与 OpenCV 一起使用也很容易。 我们发布此框架是因为我们对这种通用图像识别方法的强大功能感到...

    Countly移动分析应用-其他

    6、iOS和Android的崩溃报告以及Javascript的错误报告 7、适用于iOS和Android的丰富且交互式的推送通知 8、电子邮件报告 Countly特点: 1、实时移动分析,Web分析和推送通知。 2、您的数据,规则-因为您可以在自己的...

    chrome.exe

    另有手机版的Chrome浏览器,于2012年发布了Chrome浏览器移动版,提供IOS系统、安卓系统以及Windows Phone系统的Chrome浏览器,在保持浏览器原有特点的情况下,实现了多终端使用浏览器,具有共享收藏历史信息等功能,...

Global site tag (gtag.js) - Google Analytics