Новый iPad: предупреждения о нехватке памяти? - PullRequest
21 голосов
/ 18 марта 2012

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

Я прочитал несколько похожих вопросов:

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

Кажется, ни одно из предложений не помогает.

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

[[UIApplication sharedApplication] _performMemoryWarning];

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

Ответы [ 6 ]

12 голосов
/ 04 апреля 2012

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

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

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

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

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

Может быть, это помогает ...

2 голосов
/ 08 мая 2012

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

Сначала я создал категорию на UIApplication.После этого UIImage

.h

@interface UIApplication (ForceLowMemory)

+ (void) forceUnload;

@end

публикует уведомление, которое *1004* прослушивает (или какой-либо другой его резервный кеш).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
1 голос
/ 03 апреля 2012

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

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

1 голос
/ 23 марта 2012

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

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

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

[UIImage imageNamed:(NSString *)]

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

[UIIMage imageWithContentsOfFile:(NSString *)]

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

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

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

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

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

0 голосов
/ 02 мая 2012

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

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

0 голосов
/ 23 марта 2012

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

  1. Используйте alloc - init и release , избегайте использования объектов автоматического выпуска.
  2. Проверка на утечки памяти
  3. Если вам нужно использовать объект autorelease, используйте NSAutoreleasePool перед созданием объекта и сливом пула после завершения работы с объектом.
  4. Если вы используете OpenGL , не забудьте удалить все созданные объекты, особенно текстуры
  5. Может быть, вам следует попробовать перейти на ARC
...