2013-10-23 9 views
8

Usiamo NSCache per UIImages nella nostra app. Funziona perfettamente su versioni iOS inferiori a 7. Quando si verifica un avviso di memoria, NSCache rilascia oggetti come previsto. Tuttavia, su iOS 7, la nostra app si blocca poco dopo il primo avviso di memoria. Sembra quindi che gli oggetti archiviati con NSCache non vengano mai rilasciati, ma la cache sta crescendo fino a quando l'app non si arresta. La profilazione con strumenti conferma questo sospetto.Arresto anomalo di NSCache quando viene raggiunto il limite di memoria (solo su iOS 7)

Qualcun altro ha riscontrato questo problema e ha trovato una soluzione alternativa o ha già tracciato un bug?

Sembra che quei ragazzi avevano lo stesso problema: http://www.photosmithapp.com/index.php/2013/10/photosmith-3-0-2-photo-caching-and-ios-7/

ho creato una piccola applicazione di esempio per illustrare il problema. Quando viene premuto un pulsante, viene chiamato il metodo -(IBAction)fillCache:(id)sender. Da quel momento in poi, un timer chiama -(void)addImageToCache:(id)sender ogni 100 ms. In questo metodo, un UIImage viene generato e scritto nella cache.

Sull'iPad Mini con iOS 7.0.3 e la sua memoria da 512 MB, si blocca dopo ~ 350 iterazioni.

Sull'iPad 2 con iOS 5 e anche 512 MB di memoria, si arresta anche a un certo punto, ma solo dopo almeno 3000 iterazioni. Gli strumenti mostrano che il numero di istanze UIImage diminuisce ogni volta che si verifica un avviso di memoria. Questo non è il caso su iOS 7.

- (IBAction)fillCache:(id)sender 
{ 
    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(addImageToCache:) userInfo:nil repeats:YES]; 
} 

- (void)addImageToCache:(id)sender 
{ 
    @autoreleasepool { 

     CGRect rect = CGRectMake(0, 0, 500, 500); 
     UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0); 
     UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 
     UIGraphicsEndImageContext(); 

     NSString *poolKey = [NSString stringWithFormat:@"junk_%d", count++]; 
     [self.cache setObject:image forKey:poolKey]; 

    } 
} 

risposta

15

Mentre NSCache non ha mai risposto agli avvisi di memoria, ho scoperto che generalmente rispondeva alla vera pressione della memoria. L'incapacità di rispondere agli avvertimenti della memoria è sempre stata un po 'fastidiosa (ad esempio non si poteva semplicemente usare il "simulate memory warning" per testare il comportamento di un'app in pressione di memoria).

Detto questo, vedo lo stesso comportamento che descrivi. iOS 7 sembra aver cambiato il comportamento di NSCache.

Personalmente, non mi resta che ingenuo NSCache sottoclasse che proprio rimuove tutti gli oggetti dopo aver ricevuto la notifica di UIApplicationDidReceiveMemoryWarningNotification:

@implementation AutoPurgeCache 

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; 
} 

@end 
+1

questo sembra sano e bene ... mi chiedo se è un po 'la mano pesante , potresti ... ridurre invece il valore TotalCostLimit o CountLimit? –

+1

@GradyPlayer Concordato. Mi piace la tua idea, ma non ero sicuro di come potresti essere sicuro che modificare i limiti come hai suggerito sarebbe sufficiente per ripristinare l'app in una situazione stabile. Si potrebbero anche impostare limiti ragionevoli in anticipo e potrebbe prevenire l'allarme memoria che si verifica in primo luogo.Ma se lo fai e ricevi ancora avvertimenti sulla memoria, allora questo approccio pesante potrebbe essere un baluardo contro un fallimento catastrofico. – Rob

+1

probabilmente hai ragione, non so se ci vuole un giro o due attraverso il runloop ... Suppongo che non dovresti avere niente lì dentro che non puoi ricreare più tardi. –

3

L'oggetto NSCache rimuove i dati basandosi sulle proprie regole. Ciò non significa che rilascerà il contenuto durante un avviso di memoria.
Ecco ciò che dice la doc:

La classe NSCache incorpora diverse politiche di auto-sfratto, che garantire che una cache non usa troppa memoria del sistema. Se la memoria è necessaria per altre applicazioni, queste politiche rimuovono dalla cache alcuni elementi , riducendo al minimo il suo ingombro di memoria.

Molto probabilmente sono state modificate alcune norme in iOS7. È possibile rimuovere tutti i contenuti ascoltando la notifica di avviso di memoria. Collego questo answer per completezza.

Problemi correlati