2012-03-17 10 views
20

Ho sviluppato un'applicazione per iPad molto intensa dal punto di vista grafico. Sono stato in grado di spremere un bel po 'di prestazioni già sull'iPad 2, ma la grafica @ 2x per il nuovo iPad sta facendo il pieno nel reparto di memoria. Uso del monitor Attività in Strumenti Sono in grado di vedere le dimensioni della mia applicazione aumentare rapidamente nell'intervallo 300MB-400MB, ma non ricevo notifiche di memoria insufficiente. Sto usando un UINavigationController per gestire le mie visualizzazioni, quindi scendere nello stack ha un effetto cumulativo sulla memoria che termina con la sua eventuale conclusione. Non ho riscontrato questo problema su iPad 2, dove ricevo notifiche di memoria insufficiente come previsto. La mia app è stata codificata per pulire il più possibile e funziona molto bene su quel dispositivo.Il nuovo iPad: gli avvisi di scarsa memoria non appaiono?

Ho letto una serie di domande simili chiesto:

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

Nessuno dei suggerimenti sembra aiutare.

ho inserito il codice per forzare una notifica memoria insufficiente da inviare:

[[UIApplication sharedApplication] _performMemoryWarning]; 

Ciò provoca le viste inattivi per scaricare come previsto e ritorna consumo di memoria normale. Questo utilizza un'API privata e hack, quindi per motivi pratici non è una soluzione. Come faccio a far sì che il mio dispositivo risponda correttamente alle condizioni di memoria insufficiente e informi la mia app che è necessario pulire?

+0

Avete testato con iOS 5.1 su iPad 2, così? –

+0

@ jdv-JandeVaan Sì. –

+0

Puoi confermare che sia con gli iPad che con lo stesso iOS 5.1, quando carichi esattamente la stessa sequenza di immagini (\ @ 1x per iPad2 e \ @ 2x per iPad3) e guardi i controller l'app termina su iPad3 e non termina su iPad2? e un altro test, se vi sbarazzate delle immagini @ 2x, riceverete su iPad3 (ancora una volta verranno caricate le immagini ovviamente) gli avvisi di memoria o l'app terminerà ancora? – viggio24

risposta

2

Questo problema è stato risolto in iOS 5.1.1. Per quegli utenti che sono in 5.1, ho implementato il mio watchdog di memoria e inviando una notifica simile a quella usata quando viene emesso un avviso di memoria reale.

Ho prima creato una categoria su UIApplication. Questo invia una notifica che ascolta UIImage (o qualunque sia la sua cache di supporto) per scaricare le immagini memorizzate nella cache.

.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 

Successivamente, ho creato un cane da guardia gestore di memoria come la seguente:

.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 
+0

Se inserisco un breakpoint nel mio ReceMemoryWarning del mio AppDelegate: (o qualunque sia la firma), non verrà chiamato se invio la notifica che usi. Stesso per te? – Krumelur

1

Il seguito è la mia esperienza. Sono abbastanza felice di essere corretto ...

Come stai caricando le tue immagini?

Se si sta utilizzando:

[UIImage imageNamed:(NSString *)] 

quindi è necessario fare in modo che ci sia un buon motivo per farlo. Se stai facendo un uso pesante di un'immagine che deve essere memorizzata nella cache, allora è una buona opzione. In caso contrario, vorrei suggerire di usare

[UIIMage imageWithContentsOfFile:(NSString *)] 

iOS sembra avere alcuni problemi di rilascio di immagini caricate tramite imageNamed, anche se non ci sono più riferimenti ad esso. Poiché la tua app non avrà più riferimenti all'immagine, probabilmente non riceverai gli avvisi sulla memoria. Ma ciò non significa che la memoria sia stata liberata. iOS tende a mantenere queste immagini nella memoria molto più a lungo di quanto desideri. Quando normalmente darebbe un avviso di memoria, terminerà semplicemente l'app.

Si consiglia inoltre di attivare il conteggio riferimento automatico (ARC).

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

Ho avuto problemi simili per un po 'di tempo. Apportare le modifiche di cui sopra alla mia app ha bloccato gli arresti anomali e interrotto la perdita di memoria causata dal ricaricamento della stessa immagine più e più volte tramite imageNamed.

Spero che questo aiuti.

+0

C'era una perdita in ** [UIImage imageNamed:] ** metodo in ios 2.x ma è stato riparato in ios 3.0 –

+0

Le immagini sono caricate tramite IB. –

+0

Forse potrebbe essere il problema. –

0

Ho lavorato sul app che consumano molta memoria ed i miei suggerimenti sono:

  1. Usa alloc - init e rilasciano, evitare l'uso di oggetti autorelease.
  2. Verificare la presenza di perdite di memoria
  3. Se è necessario utilizzare l'oggetto autorelease, utilizzare NSAutoreleasePool prima di creare l'oggetto e svuotare la piscina quando avete finito il lavoro con l'oggetto.
  4. Se si utilizza OpenGL ricordarsi di eliminare tutti gli oggetti creati, in particolare texture
  5. Forse si dovrebbe cercare di passare ad ARC
+0

Principalmente uso alloc/init, ho 0 perdite, e quelle istanze, se giustificate, usano un pool di autorelease. –

+0

Solo una nota a margine per dire che sto usando ARC con un progetto che impiega grandi bitmap 2X dappertutto e devo ancora vedere un basso errore di mem (o crash) sull'iPad di terza generazione. Sono molto impressionato con ARC a questo punto, dopo un inizio roccioso. –

1

Sembra a me come il problema è che le immagini non sono essere rilasciato. Secondo lo UIImage documentation (e la mia esperienza), imageNamed: memorizza nella cache le immagini che carica. Pertanto dovresti usarlo per piccole icone o immagini che sono quasi costantemente utilizzate, ma in genere è una cattiva idea usarlo per immagini di grandi dimensioni o immagini che vengono utilizzate di rado. Come user499177 dice, si dovrebbe usare imageWithContentsOfFile: (è possibile utilizzare [NSBundle mainBundle] metodi per ricavare il percorso), invece:

Questo metodo appare nelle cache di sistema per un oggetto immagine con il nome e ritorna quell'oggetto se esiste specificato. Se un oggetto immagine corrispondente non è già presente nella cache, questo metodo carica i dati dell'immagine dal file specificato, li memorizza nella cache e quindi restituisce l'oggetto risultante.

+0

Il 99,99% delle mie immagini viene caricato tramite XIB. –

+0

Sono abbastanza sicuro che il caricamento XIB utilizza la cache. Se hai molte viste tutte con (ad esempio) sfondi diversi, questo mangerà velocemente la tua RAM disponibile. – Benjie

11

ho contattato il supporto Apple per risolvere il mio problema di memoria e ha chiesto circa le avvertenze di memoria insufficiente su iPad 3:

avvisi di memoria -perché vengono consegnati sul thread principale, l'applicazione non parte ricevere avvisi di memoria se sta bloccando il thread principale.

-Anche se la vostra applicazione non stia bloccando il thread principale, è possibile per la memoria utilizzo di crescere abbastanza in fretta che gli avvisi di memoria non sono consegnati prima la vostra applicazione viene ucciso per liberare la memoria.

- Avvisi di avviso memoria quando il kernel esegue la transizione tra vari livelli di pressione di memoria . Per questo motivo, è comune che un'app riceva un avviso di memoria e venga poi eliminata un po 'di tempo dopo l'esaurimento della memoria. L'avviso di memoria iniziale ha liberato memoria sufficiente a mantenere l'app in vita, ma non a per consentire al kernel di passare a un livello inferiore di pressione della memoria.

A causa di tutto questo, avviso di memoria devono essere trattati come dati utili sulla stato dell'hardware e una buona guida sulla quantità di memoria la vostra applicazione dovrebbe usare su un determinato dispositivo, ma non devono essere considerate come uno strumento per impedire che l'app venga uccisa.

Forse questo aiuta ...

+0

Non ricevo alcun avviso di memoria. Ho ridotto il blocco del thread principale ad un minimo assoluto. Penso che il problema qui sia che c'è un margine piuttosto stretto tra la quantità di memoria che la mia app sta utilizzando e quando si verifica una condizione di memoria e il caricamento dei miei xib pesanti supera quel gap e termina senza preavviso. –

+0

Ho creato un'app di test, che ha solo danneggiato la memoria eseguendo un ciclo while e assegnando un oggetto NSData. Ricevo avvisi su iPad 2 ma non su iPad 3 con la stessa app. – robbash

0

Mega-Dittos. Questo è stato molto rassicurante.Anch'io faccio un'app intensa di immagini (un sacco di oggetti UIImage in UIImageView per l'animazione) e ho il solito codice di avviso di memoria nel mio delegato dell'app ma non viene mai attivato. Ciononostante, gli strumenti hanno mostrato il problema mentre caricavo le immagini, le disegnavo e le affidavo alle immagini. Sto usando ARC, mi sono liberato delle perdite da CGImageRef s e simili, ma alla fine della giornata se carichi abbastanza immagini in un ImageView, abbastanza rapidamente si verificherà un arresto anomalo senza alcun tipo di avviso nel registro, app delegare metodo callback o strumenti. L'app si toglie il tappeto da sotto senza nemmeno un "congedo".

Non ho ancora avuto la possibilità di provare questo su un iPad2, ma a prescindere, ci deve essere un'indicazione SOME, almeno un messaggio di console minimalista o qualcosa del genere. La maggior parte del caricamento avviene sulla mia coda GCD, non sul thread principale sebbene per definizione gli aggiornamenti ai controlli dello schermo debbano essere eseguiti sul thread principale. Quindi supero se questo è un blocco quando la corsa sta per essere tirata, suppongo che tu possa ottenere un errore anonimo. Certo sarebbe di aiuto per questo per ottenere qualche tipo di messaggio di console però.

+0

Ho fatto ricorso alla creazione del mio gestore memoria personale e alla pubblicazione manuale della notifica che provoca lo scarico delle viste inattive e UIImage allo svuotamento della cache dell'immagine. Ho ancora incidenti sporadici quando, come te, provo a caricarne troppi contemporaneamente su un thread in background. Potrebbe esserci un processo di watchdog Apple che protegge da improvvisi picchi di memoria, anche se non hai tecnicamente raggiunto la soglia massima di memoria. –

Problemi correlati