2013-10-23 8 views
8

Wir verwenden NSCache für UIImages in unserer App. Dies funktioniert auf iOS-Versionen kleiner als 7. Wenn eine Speicherwarnung auftritt, gibt NSCache Objekte wie vorgesehen frei. Unter iOS 7 stürzt unsere App jedoch kurz nach der ersten Speicherwarnung ab. Es sieht also so aus, als würden Objekte, die mit NSCache gespeichert wurden, nie freigegeben, aber der Cache wächst, bis die App abstürzt. Profiling mit Instrumenten bestätigt diesen Verdacht.NSCache stürzt ab, wenn das Speicherlimit erreicht ist (nur unter iOS 7)

Hatte jemand anderes dieses Problem und haben Sie einen Workaround gefunden oder haben Sie bereits einen Fehler gefunden?

Es sieht aus wie die Jungs das gleiche Problem hatte: http://www.photosmithapp.com/index.php/2013/10/photosmith-3-0-2-photo-caching-and-ios-7/

ich eine kleine Beispielanwendung erstellt, das Problem zu veranschaulichen. Wenn eine Taste gedrückt wird, wird die Methode -(IBAction)fillCache:(id)sender aufgerufen. Von nun an ruft ein Timer alle 100 ms -(void)addImageToCache:(id)sender auf. In dieser Methode wird ein UIImage generiert und in den Cache geschrieben.

Auf dem iPad Mini mit iOS 7.0.3 und seinem 512 MB Speicher stürzt es nach ~ 350 Iterationen ab.

Auf dem iPad 2 mit iOS 5 und auch 512 MB Speicher stürzt es irgendwann ab, aber erst nach mindestens 3000 Iterationen. Instruments zeigt, dass die Anzahl der UIImage-Instanzen bei jeder Speicherwarnung abnimmt. Dies ist unter iOS 7 nicht der Fall.

- (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]; 

    } 
} 

Antwort

15

Während NSCache nie auf Speicherwarnungen reagierte, fand ich, dass es im Allgemeinen auf wahren Speicherdruck reagierte. Der Fehler, auf Speicherwarnungen zu reagieren, war schon immer ein bisschen lästig (z. B. konnte man nicht einfach die "Speicherwarnung simulieren" verwenden, um das Verhalten einer App im Speicherdruck zu testen).

Nachdem ich das gesagt habe, sehe ich das gleiche Verhalten, das Sie beschreiben. iOS 7 scheint das NSCache Verhalten geändert zu haben.

Ich persönlich habe nur einfältig NSCache Unterklasse, die nur alle Objekte entfernt nach dem Empfang der Benachrichtigung 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

diese gesund und fein scheint ... Ich frage mich, ob es ein wenig plump ist Könntest du ... stattdessen totalCostLimit oder countLimit reduzieren? –

+1

@GradyPlayer Einverstanden. Ich mag deine Idee, aber ich war mir nicht sicher, wie du sicher sein könntest, dass das Ändern der Limits, wie du es vorgeschlagen hast, ausreichen würde, um die App in eine stabile Situation zu bringen. Man könnte auch vorab vernünftige Grenzen setzen und verhindern, dass der Speicheralarm überhaupt auftritt.Aber wenn Sie das tun und immer noch Gedächtniswarnungen erhalten, dann könnte dieser unberechenbare Ansatz ein Bollwerk gegen katastrophales Versagen sein. – Rob

+1

Sie haben wahrscheinlich recht, ich weiß nicht, ob es auch ein oder zwei Drehungen durch den Runloop braucht ... Ich denke, Sie sollten nichts darin haben, das Sie später nicht neu erstellen können. –

3

Das NSCache-Objekt entfernt seine Daten basierend auf seinen eigenen Regeln. Das bedeutet nicht, dass Inhalte während einer Speicherwarnung freigegeben werden.
Hier ist, was die doc heißt es:

Die nscache Klasse umfasst verschiedene Auto-Räumungsstrategien, die sicherzustellen, dass ein Cache nicht zu viel von dem Systemspeichers nicht verwendet. Wenn Speicher von anderen Anwendungen benötigt wird, entfernen diese Richtlinien einige Elemente aus dem Cache, wodurch der Speicherbedarf minimiert wird.

Höchstwahrscheinlich die geänderten einige Richtlinien in iOS7. Sie können alle Inhalte entfernen, indem Sie Speicherwarnungsbenachrichtigungen abhören. Ich verlinke diese answer der Vollständigkeit halber.

Verwandte Themen