21

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

我读过许多类似的问题:

IOS 应用程序因内存不足而被终止,但没有收到内存警告
iPhone 应用程序使用 150 MB 内存,但仍然没有内存不足警告!

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

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

[[UIApplication sharedApplication] _performMemoryWarning];

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

4

6 回答 6

12

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

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

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

-当内核在不同级别的内存压力之间转换时会触发内存警告。因此,应用程序通常会收到内存警告,然后在一段时间后内存耗尽时被终止。初始内存警告释放了足够的内存来保持应用程序的运行,但不足以让内核过渡到较低级别的内存压力。

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

也许这有帮助...

于 2012-04-04T10:17:01.423 回答
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
于 2012-05-07T22:28:57.533 回答
1

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

你是如何加载图像的?

如果您正在使用:

[UIImage imageNamed:(NSString *)]

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

[UIIMage imageWithContentsOfFile:(NSString *)]

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

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

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

我有一段时间有类似的问题。对我的应用程序进行上述更改停止了崩溃,并停止了当我通过 imageNamed 一遍又一遍地重新加载相同的图像时引起的内存泄漏。

希望有帮助。

于 2012-03-23T05:59:05.130 回答
1

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

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

于 2012-04-03T10:34:29.443 回答
0

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

  1. 使用alloc-init 和 release,避免使用 autorelease 对象。
  2. 检查内存泄漏
  3. 如果您需要使用自动释放对象,请在创建对象之前使用NSAutoreleasePool,并在完成对象工作后清空池。
  4. 如果您使用OpenGL,请记住删除所有创建的对象,尤其是纹理
  5. 也许你应该尝试切换到 ARC
于 2012-03-23T09:22:34.360 回答
0

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

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

于 2012-05-02T16:05:59.610 回答