欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

IOS开发(44)之内存警告

程序员文章站 2022-06-01 11:43:01
我们都知道在移动设备上很多资源都是比较紧缺的,尤其时内存,通常都比较小,iphone4也才只有512mb。而且ios4.0以后还支持了多任务,这个问题就更加突出了。因此我们在平时设...

我们都知道在移动设备上很多资源都是比较紧缺的,尤其时内存,通常都比较小,iphone4也才只有512mb。而且ios4.0以后还支持了多任务,这个问题就更加突出了。因此我们在平时设计程序的时候要注意管理好内存,减少不必要的开销,谨防泄露。

  由于写的一个小项目存在严重的内存泄漏,程序经常运行时间不长就退出了,调试时候发现运行过程中接受到的memry warning level 1几次以后,紧接着收到一个memory warning level 2,然后程序退出。通常使用xcode4自带的工具,跟踪发现由于一个图像资源忘记release导致。问题解决了,但我没有打算就此带过。接着上网查找了有关memory warning level的资料,后来在*上面找到一些有用的数据,这是kennytm写的,跟大家分享一下:

    系统有四种内存警告,定义如下:

    typedef enum {

        osmemorynotificationlevelany      = -1,

        osmemorynotificationlevelnormal   =  0,

        osmemorynotificationlevelwarning  =  1,

        osmemorynotificationlevelurgent   =  2,

        osmemorynotificationlevelcritical =  3

    } osmemorynotificationlevel;

  但是这几种警告之间是怎么变化的,到达什么阈值时会从一个level跳到另外一个level,文章中没有提及。

  通常我们在程序中接收到最多的就是memory warning level 1,这个时候就证明系统内存紧张了,我们的程序必须做出相应,释放不必要的内存。通常如果我们处理得当的话,内存警告就会到此停止,恢复正常状态。如果我们在接收到memory warning level 1以后坐视不理的话,系统可能还会再尝试通知几次level 1,如果还是不去处理的话,系统将会发出更高一级的内存警告 level 2,通常的结果就是我们的app被强制退出,系统收回内存。当然系统在发出level 2的警告时,也会取尝试做一些清理内存的事,例如关闭不必要的后台程序等。很显然我们不该寄希望于系统自己清理,这就好比你的胳膊被划破了,身体肯定会有自修复功能,但是如果伤口比较大的话,肯定还是需要人工处理一下的。

  到目前位置我还没有见过level 3的警告,根据stack over flow上面讲的“当发生level 3警告时,系统内核将会接管,很有可能关掉ios的主界面进程(springborad),甚至会重启”,照这个意思来说我们程序里面接收不到,也实属正常,系统自己的东西都被干掉了,我们肯定早被kill了。

 

  如果打开上面的连接,可以看到定义osmemorynotificationlevel的源文件,你可能会注意到里面有一个函数osmemorynotificationcurrentlevel(),可以用来获取当前内存告警level,不过需要加头文件#import <libkern/osmemorynotification.h>,网上有个查看当前内存数据的开源代码memwatcher,大家可以看看,说实话我没有看懂。

  说了这么多,希望能帮大家弄清楚内存警告是怎么回事儿,通常我们程序是不会碰到内存警告,平时写代码注意在所有alloc,retain,copy的地方,对应的写上release,或者直接创建autorelease对象(个人不推荐这么做),发布前养成检查内存泄露的好习惯。

  顺便提一下如果运行过程中遇到内存警告的话,程序通常情况下都先调用appdelegate中的applicationdidreceivememorywarning, 然后程序会通知各viewcontroller,调用其didrecievememorywarning方法,这个时候我们一定要种,释放不必要的资源。

  ~~~~~~~~~~~~~~~~~~~~~~3月9号更新~~~~~~~~~~~~~~~~~~~~~~

  今天回家写了个小例子测试内存泄漏,思路如下:在uiviewcontroller的viewdidload中,向self.view添加一个自定义的专门泄漏的uiview(在初始化函数中开启一个线程不停的加载图片)。在我的iphone4下运行程序后大概不到一分钟以后出现level 1 警告,然后过10秒左右报出level 2警告,再过5秒左右程序被退出。截图如下:

 

  这也证明前面说过的level 3的警告通常我们是接收不到的这个推断。

 

svmemorywarningviewcontroller.m

////  svmemorywarningviewcontroller.m//  svmemorywarning////  created by maple on 3/9/12.//  copyright (c) 2012 smileevday. all rights reserved.//#import "svmemorywarningviewcontroller.h"#import <libkern/osmemorynotification.h>#import "svleakmemoryview.h"@interface svmemorywarningviewcontroller ()@end@implementation svmemorywarningviewcontroller- (void)viewdidload{    [super viewdidload];    // do any additional setup after loading the view, typically from a nib.        svleakmemoryview *view = [[svleakmemoryview alloc] initwithframe:cgrectmake(0, 0, 320, 480)];    [self.view addsubview:view];    [view release];}- (void)viewdidunload{    [super viewdidunload];    // release any retained subviews of the main view.}- (bool)shouldautorotatetointerfaceorientation:(uiinterfaceorientation)interfaceorientation{    return interfaceorientation == uiinterfaceorientationportrait;}- (void)didreceivememorywarning{        nslog(@"recieve memory warning");    nslog(@"~~~~~~~~~~~~~~level~~~~~~~~~~~~~~~ %d", (int)osmemorynotificationcurrentlevel());}@end
 

svleakmemoryview.m

#import "svleakmemoryview.h"#import <quartzcore/quartzcore.h>@implementation svleakmemoryview- (id)initwithframe:(cgrect)frame{    self = [super initwithframe:frame];    if (self) {        // initialization code                uiimageview *imageview = [[uiimageview alloc] initwithframe:frame];                nsstring *filepath = [[nsbundle mainbundle] pathforresource:@"car" oftype:@"jpg"];        imageview.image = [[uiimage alloc] initwithcontentsoffile:filepath];                imageview.layer.contentsgravity = kcagravityresizeaspect;        [self addsubview:imageview];                nsstring *filepath2 = [[nsbundle mainbundle] pathforresource:@"treehighresolution" oftype:@"jpg"];         dispatch_queue_t loadimagequeue = dispatch_queue_create("load image", null);                __block uiimageview *testiv = [[uiimageview alloc] initwithframe:cgrectmake(0, 0, 320, 480)];        dispatch_async(loadimagequeue, ^{            while (true) {                uiimage *image = [[uiimage alloc] initwithcontentsoffile:filepath2];                testiv.image = image;            }        });    }    return self;}/*// only override drawrect: if you perform custom drawing.// an empty implementation adversely affects performance during animation.- (void)drawrect:(cgrect)rect{    // drawing code}*/@end