Новый iPad: предупреждения о низкой памяти не появляются?

Я разрабатываю приложение для iPad, которое очень графически интенсивно. Я смог выжать довольно много производительности уже на iPad 2, но графика @2x для нового iPad упаковывает совершенно удар в отдел памяти. Используя Монитор активности в Инструментах, я могу видеть размер моего приложения, входящего в диапазон 300 МБ-400 МБ, но я не получаю никаких уведомлений о низкой памяти. Я использую UINavigationController для управления моими представлениями, поэтому попадание в стек имеет кумулятивный эффект на память, который заканчивается его окончательным завершением. Я не испытываю эту проблему на iPad 2, где я получаю уведомления о низкой памяти, как ожидалось. Мое приложение было закодировано для очистки как можно больше и очень хорошо работает на этом устройстве.

Я прочитал ряд подобных вопросов:

Приложение IOS было убито за низкую память, но не получено предупреждение о сохранении памяти
Приложение iPhone использует память 150 МБ и все еще не содержит предупреждения о низкой памяти!

Ни одно из предложений, похоже, не помогает.

Я вставил код, чтобы отправить уведомление с низкой памятью:

[[UIApplication sharedApplication] _performMemoryWarning];

Это приводит к тому, что неактивные представления выгружаются так, как ожидалось, и возвращают потребление памяти в нормальное состояние. Это использует частный API и взломан, поэтому по практическим соображениям это не решение. Как заставить устройство правильно реагировать на условия низкой памяти и сообщить моему приложению, что ему нужно очистить

Ответ 1

Эта проблема была исправлена ​​в iOS 5.1.1. Для тех пользователей, которые в 5.1, я реализовал свой собственный сторожевой таймер и отправил уведомление, подобное тому, которое используется при выдаче предупреждения о реальной памяти.

Сначала я создал категорию на UIApplication. Это отправляет уведомление о том, что UIImage прослушивает (или что-то еще его кеш файл) для выгрузки кэшированных изображений.

.h

@interface UIApplication (ForceLowMemory)

+ (void) forceUnload;

@end

ого

#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

ого

#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

Ответ 2

Я связался с Apple Support для решения моей проблемы с памятью и спросил о предупреждениях с низкой памятью на iPad 3:

-Потому что предупреждения памяти доставляются по основному потоку, ваше приложение не будет получать предупреждения о памяти, если они блокируют основной поток.

- Даже если ваше приложение не блокирует основной поток, возможно для памяти использования, чтобы быстро расти, чтобы предупреждения памяти не доставлялись раньше ваше приложение убито для освобождения памяти.

-Memory предупреждает, когда ядро ​​переходит между различными уровнями давление памяти. Из-за этого обычно приложение получает память предупреждение, а затем быть убитым довольно некоторое время спустя, когда память исчерпана. Первоначальное предупреждение о памяти освобождает достаточно памяти, чтобы поддерживать приложение в живом режиме, но не достаточно для перехода ядра на более низкий уровень давления памяти.

Из-за этого предупреждение о памяти следует рассматривать как полезные данные о состояние оборудования и хорошее руководство по тому, сколько памяти должно использовать ваше приложение на данном устройстве, но на него нельзя полагаться как инструмент для предотвращения вашего приложения от убийства.

Возможно, это помогает...

Ответ 3

Ниже мой опыт. Я очень рад, что меня исправили...

Как вы загружаете свои изображения?

Если вы используете:

[UIImage imageNamed:(NSString *)]

Тогда вам нужно убедиться, что есть веская причина. Если вы сильно используете изображение, которое нужно кэшировать, то это хороший вариант. В противном случае я предлагаю вам использовать

[UIIMage imageWithContentsOfFile:(NSString *)]

В iOS есть некоторые проблемы, связанные с выпуском изображений, загружаемых с помощью imageNamed, даже если на них больше нет ссылок. Поскольку ваше приложение больше не будет ссылаться на изображение, вы, вероятно, не получите предупреждения о памяти. Но это не означает, что память была освобождена. iOS имеет тенденцию сохранять эти изображения в памяти намного дольше, чем хотелось бы. Когда он обычно выдаст предупреждение о памяти, он просто прекратит приложение.

Я также настоятельно рекомендую включить автоматический подсчет ссылок (ARC).

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

У меня были подобные проблемы довольно долго. Выполнение вышеуказанных изменений в моем приложении остановило аварийные ситуации и остановило утечку памяти, вызванную повторным загрузкой одного и того же изображения через imageNamed.

Надеюсь, что это поможет.

Ответ 4

Мне кажется, что проблема заключается в том, что ваши изображения не выпускаются. Согласно документации UIImage (и моему опыту), imageNamed: кэширует изображения, которые он загружает. Поэтому вы должны использовать его для небольших значков или изображений, которые почти постоянно используются, но обычно это плохая идея использовать его для больших изображений или изображений, которые используются нечасто. Как говорит user499177, вы должны использовать imageWithContentsOfFile: (вы можете использовать методы [NSBundle mainBundle] для получения пути):

Этот метод просматривает системные кэши для объекта изображения с указанным именем и возвращает этот объект, если он существует. Если соответствующий объект изображения еще не находится в кеше, этот метод загружает данные изображения из указанного файла, кэширует его и затем возвращает результирующий объект.

Ответ 5

Я работаю над приложением, потребляющим много памяти, и мои предложения:

  • Используйте alloc - init и release, избегайте использования объектов autorelease.
  • Проверьте наличие утечек памяти
  • Если вам нужно использовать объект автоопределения, используйте NSAutoreleasePool, прежде чем создавать объект, и слейте пул, когда закончите работу с объектом.
  • Если вы используете OpenGL, не забудьте удалить все созданные объекты, особенно текстуры
  • Возможно, вам стоит попробовать переключиться на ARC

Ответ 6

Мега-Dittos. Это было очень обнадеживающим. Я тоже делаю приложение с интенсивным изображением (много UIImage объектов в UIImageView для анимации) и имеет обычный код предупреждения о памяти в своем делете приложения, но он никогда не запускается. Тем не менее, инструменты показали проблему, когда я загружал изображения, рисовал их и передавал их изображениям. Я использую ARC, избавился от утечек из CGImageRef и тому подобного, но в конце дня, если вы загрузите достаточное количество изображений в ImageView, достаточно быстро вы получите аварийный сигнал без предупреждения в журнале, обратный вызов метода делегата приложения или инструменты. Приложение просто достает ковер, вытащенный из-под него, не так много, как "по вашему желанию".

У вас не было возможности попробовать это на iPad2, но, несмотря на это, должно быть НЕКОТОРНОЕ указание, по крайней мере минимальное консольное сообщение или что-то еще. Большая часть загрузки происходит в моей собственной очереди GCD, а не в основном потоке, хотя по определению обновления для элементов управления экраном должны выполняться в основном потоке. Таким образом, я supupose, если эта блокировка, когда запуск будет вытащен, я думаю, вы можете просто получить анонимный сбой. Несомненно, это помогло бы получить небольшое сообщение консоли.