新 iPad:内存不足警告未出现?

Posted

技术标签:

【中文标题】新 iPad:内存不足警告未出现?【英文标题】:The New iPad: Low Memory Warnings Not Appearing? 【发布时间】:2012-04-02 23:10:32 【问题描述】:

我一直在为 iPad 开发一个图形密集型应用程序。我已经能够在 iPad 2 上挤出相当多的性能,但新 iPad 的@2x 图形在内存部门中占据了相当大的优势。使用 Instruments 中的活动监视器,我可以看到我的应用程序的大小迅速增长到 300MB-400MB 范围,但我没有收到任何内存不足的通知。我正在使用UINavigationController 来管理我的视图,因此深入堆栈会对内存产生累积影响,最终会终止。我在 iPad 2 上没有遇到这个问题,我收到了预期的内存不足通知。我的应用程序已被编码为尽可能多地清理并在该设备上运行良好。

我已经阅读了许多类似的问题:

IOS app killed for Low Memory but no Memory Warning received iPhone app uses 150 MB memory and still no low memory warning!

这些建议似乎都没有帮助。

我已插入代码以强制发送内存不足通知:

[[UIApplication sharedApplication] _performMemoryWarning];

这确实会导致非活动视图按预期卸载并将内存消耗恢复正常。这使用私有 API 并且是 hack,因此出于实际原因不是解决方案。 如何让我的设备正确响应内存不足的情况并让我的应用知道它需要清理?

【问题讨论】:

您是否也在 iPad 2 上使用 ios 5.1 进行过测试? 您能否确认使用 iPad 和相同的 iOS 5.1,当您加载完全相同的图像序列(iPad2 为 \@1x,iPad3 为 \@2x)并查看控制器时,应用程序终止iPad3 并且不在 iPad2 上终止?还有另一个测试,如果你去掉@2x 图像,你会在 iPad3 上收到内存警告(当然是加载更多图像)还是应用程序仍会终止? 我可以确认相同的事件序列在 iPad 2(运行 5.1)上可以正常工作,但在 iPad 3 上会导致崩溃。我必须在 iPad 2 上非常辱骂才能强制它进入内存不足的情况。 当我删除所有 @2x 图像时,我的应用程序运行时占用与 iPad 2 相同的空间并且运行良好。 【参考方案1】:

超级同上。这让人非常放心。我也在做一个图像密集型应用程序(UIImageView 中有很多 UIImage 对象用于动画)并且在我的应用程序委托中有通常的内存警告代码,但它从未被触发。尽管如此,在我加载图像、绘制图像并将它们提交给图像视图时,Instruments 显示了问题。我正在使用 ARC,消除了来自CGImageRefs 等的泄漏,但归根结底,如果你在 ImageView 中加载了足够多的图像,那么很快你就会崩溃,而不会在日志中发出任何警告、应用程序委托方法回调或工具。该应用程序只是将地毯从它下面拉出来,而没有“请假”。

还没有机会在 iPad2 上尝试这个,但无论如何,需要有一些指示,至少是一个极简的控制台消息或其他东西。大多数加载都发生在我自己的 GCD 队列上,而不是主线程,尽管根据定义,屏幕控件的更新必须在主线程上完成。所以我假设如果在运行即将被拉动时这会阻塞,那么我猜你可能会得到一个匿名失败。当然,这有助于获得某种控制台消息。

【讨论】:

我已经求助于创建自己的内存管理器并手动发布导致非活动视图卸载和 UIImage 刷新图像缓存的通知。当我像你一样尝试在后台线程上一次加载太多时,我仍然会遇到零星的崩溃。即使从技术上讲,您还没有达到最大内存阈值,也可能有一些 Apple 看门狗进程可以防止内存突然出现峰值。【参考方案2】:

此问题已在 iOS 5.1.1 中修复。对于那些使用 5.1 的用户,我已经实现了自己的内存看门狗,并发出了类似于发出真实内存警告时使用的通知。

我首先在UIApplication 上创建了一个类别。这会发布一条通知,UIImage 会侦听(或其后备缓存是什么)以卸载缓存的图像。

.h

@interface UIApplication (ForceLowMemory)

+ (void) forceUnload;

@end

.m

#import "UIApplication+ForceLowMemory.h"

@implementation UIApplication (ForceLowMemory)

+ (void)forceUnload 
    [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification 
                                                        object:[UIApplication sharedApplication]];


@end

接下来,我创建了一个内存管理器看门狗,如下所示:

.h

@interface ELMemoryManager : NSObject

- (void)startObserving;
- (void)stopObserving;

+ (ELMemoryManager*) sharedInstance;

@end

.m

#import "ELMemoryManager.h"
#import "UIApplication+ForceLowMemory.h"
#import <mach/mach.h>

@interface ELMemoryManager()

@property (nonatomic, retain) NSTimer *timer;
uint report_memory(void);

@end

#define MAX_MEM_SIZE 475000000

@implementation ELMemoryManager
@synthesize timer = timer_;

static ELMemoryManager* manager;

#pragma mark - Singleton

+ (void) initialize 
    if (manager == nil) 
        manager = [[ELMemoryManager alloc] init];
    


+ (ELMemoryManager*) sharedInstance 
    return manager;


#pragma mark - Instance Methods

uint report_memory(void) 
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(),
                                   TASK_BASIC_INFO,
                                   (task_info_t)&info,
                                   &size);
    if( kerr == KERN_SUCCESS ) 
        return info.resident_size;
     else 
        return 0;
    


- (void)startObserving 
    if (!self.timer) 
        NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:@selector(checkMemory:) userInfo:nil repeats:YES];
        self.timer = timer;
    
    [self.timer fire];


- (void)stopObserving 
    [self.timer invalidate];
    self.timer = nil;


- (void)checkMemory:(id)sender 
    uint size = report_memory();
    if (size > MAX_MEM_SIZE) 
        NSLog(@"we've busted the upper limit!!!");
        [UIApplication forceUnload];
    


#pragma mark - Memory Management
- (void)dealloc 
    [self.timer invalidate];
    [timer_ release];
    [super dealloc];


@end

【讨论】:

如果我在我的 AppDelegate 的 receiveMemoryWarning: 中放置一个断点:(或任何签名),如果我发送您使用的通知,它将不会被调用。你也一样?【参考方案3】:

我联系了 Apple 支持以解决我的内存问题,并询问了 iPad 3 上的内存不足警告:

-因为内存警告是在主线程上传递的,所以你的应用不会 如果它阻塞了主线程,则会收到内存警告。

-即使你的应用没有阻塞主线程,内存也是可能的 使用量增长得足够快,以至于之前不会发出内存警告 你的应用被杀死以释放内存。

-内核在不同级别的转换时触发内存警告 记忆压力。因此,应用程序接收内存是很常见的 警告,然后在相当长的一段时间后内存耗尽时被杀死。 初始内存警告释放了足够的内存以使应用程序保持活动状态,但没有 足以让内核过渡到较低级别的内存压力。

正因为如此,内存警告应该被视为有用的数据 硬件状态和应用程序应该使用多少内存的良好指南 在给定的设备上,但不应将其作为阻止您的应用程序的工具 被杀。

也许这会有所帮助...

【讨论】:

我没有收到任何内存警告。我已将我的主线程阻塞减少到绝对最低限度。我认为这里的问题是,在我的应用程序使用多少内存与发生内存条件和加载我沉重的 xib 超过该差距并在没有警告的情况下终止之间存在相当小的差距。 我创建了一个测试应用程序,它只是通过运行一个 while 循环并分配一个 NSData 对象来搞乱内存。我在 iPad 2 上收到警告,但在 iPad 3 上却没有。【参考方案4】:

在我看来,问题在于您的图像没有被发布。根据UIImage documentation(和我的经验),imageNamed: 会缓存它加载的图像。因此,您应该将它用于几乎经常使用的小图标或图像,但将其用于大图像或不经常使用的图像通常是一个坏主意。正如 user499177 所说,您应该改用imageWithContentsOfFile:(您可以使用[NSBundle mainBundle] 方法来导出路径):

此方法在系统缓存中查找具有指定名称的图像对象,如果存在则返回该对象。如果缓存中没有匹配的图像对象,则此方法从指定文件加载图像数据,缓存它,然后返回结果对象。

【讨论】:

99.99% 的图片是通过 XIB 加载的。 我很确定 XIB 加载使用缓存。如果您有很多具有(例如)不同背景的视图,那么这将很快耗尽您的可用 RAM。【参考方案5】:

我一直在开发消耗大量内存的应用程序,我的建议是:

    使用alloc - init and release,避免使用自动释放对象。 检查内存泄漏 如果您需要使用自动释放对象,请在创建对象之前使用 NSAutoreleasePool,并在完成对象工作后清空池。 如果您使用 OpenGL,请记住删除所有创建的对象,尤其是纹理 也许您应该尝试切换到 ARC

【讨论】:

我主要使用 alloc/init,有 0 个泄漏,并且那些有保证的实例使用自动释放池。 顺便说一句,我正在将 ARC 用于一个在整个地方都使用大型 2X 位图的项目,而我还没有看到第三代内存不足错误(或崩溃) iPad。在经历了艰难的开端之后,我对 ARC 印象深刻。【参考方案6】:

以下是我的经验。我很高兴得到纠正...

你是如何加载你的图片的?

如果您正在使用:

[UIImage imageNamed:(NSString *)]

那么您需要确保有充分的理由这样做。如果您正在大量使用需要缓存的图像,那么它是一个不错的选择。否则我建议你使用

[UIIMage imageWithContentsOfFile:(NSString *)]

iOS 似乎在发布通过 imageNamed 加载的图像时存在一些问题,即使不再有任何对它的引用。由于您的应用程序将不再对图像有任何引用,因此您可能不会收到内存警告。但这并不意味着内存已被释放。 iOS 倾向于将这些图像保存在内存中的时间比您想要的要长得多。当它通常会发出内存警告时,它只会终止应用程序。

我还强烈建议开启自动引用计数 (ARC)。

Edit -> Refactor -> Convert to Objective-C ARC...

很长一段时间以来我都有类似的问题。对我的应用进行上述更改可以停止崩溃,并停止当我通过 imageNamed 一遍又一遍地重新加载相同的图像时导致的内存泄漏。

希望对您有所帮助。

【讨论】:

ios 2.x 中的 [UIImage imageNamed:] 方法存在泄漏,但在 ios 3.0 中已修复 图片是通过IB加载的。 可能是问题所在。 @MartinPilch - 我遇到的 [UIImage imageNamed:] 问题发生在 5.0 和 5.1 中 - 当我加载和删除大屏幕(全屏)时一遍又一遍的图像(在轮播中)。我只会加载当前的上一个和下一个图像。一旦它们离开屏幕,我就会卸载它们(将它们交换为占位符图像)。当它们返回到三个位置中的任何一个时,我会再次加载它们。使用 [UIImage imageNamed:] 多次执行此操作会导致可用内存减少到零,并且应用程序崩溃(没有内存警告) @user499177 我遇到了同样的问题。这是因为自动释放对象基本上是泄漏的。我建议您使用 NSAutoreleasePool 并在您完成图像处理后将其排空。

以上是关于新 iPad:内存不足警告未出现?的主要内容,如果未能解决你的问题,请参考以下文章

应用程序收到内存不足警告但只有 5.7MB 的活动字节

为啥在屏幕上绘制图像之前不会出现内存不足的问题?

使用仪器解决内存不足警告

Instruments 指出内存不足警告,但内存使用率非常低

UIImageView 不释放内存(根据 VM Tracker) - 内存不足警告和终止

为啥我的 C# 应用程序中出现内存不足异常?