NSCache contient un pointeur fort vers UIImage instancié avec imageWithData: et ne supprime pas de la memory lors du déchargement

J'ai un controller de vue avec une propriété galleryCache et quand une image est téléchargée en utilisant GCD et imageWithData: l'image est ajoutée au cache avec succès avec une key. Cependant, lorsque le controller de vue est fermé, il conserve de puissants pointeurs vers ces images téléchargées, ce qui ne permet pas de les retirer de la memory. Même si j'utilise la méthode removeAllObjects sur le cache dans viewDidDisappear: memory ne s'efface pas.

Est-ce que quelqu'un sait pourquoi cela pourrait être?

Voici le code de la méthode qui télécharge les images.

 - (void)imageForFootageSize:(FootageSize)footageSize withCompletionHandler:(void (^)(UIImage *image))completionBlock { if (completionBlock) { __block UIImage *image; // Try getting local image from disk. // __block NSURL *imageURL = [self localURLForFootageSize:footageSize]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ image = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]]; dispatch_async(dispatch_get_main_queue(), ^{ if (image) { completionBlock(image); } else { // // Otherwise try getting remote image. // imageURL = [self remoteURLForFootageSize:footageSize]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSData *imageData = [NSData dataWithContentsOfURL:imageURL]; dispatch_async(dispatch_get_main_queue(), ^{ image = [UIImage imageWithData:imageData]; if (image) { // // Save remote image to disk // NSURL *photoDirectoryURL = [Footage localURLForDirectory]; // Create the folder(s) where the photos are stored. // [[NSFileManager defaultManager] createDirectoryAtPath:[photoDirectoryURL path] withIntermediateDirectories:YES atsortingbutes:nil error:nil]; // Save photo // NSSsortingng *localPath = [[self localURLForFootageSize:footageSize] path]; [imageData writeToFile:localPath atomically:YES]; } completionBlock(image); }); }); } }); }); } } 

Méthodes qui utilisent la méthode de class ci-dessus pour UIImage et traiter l' UIImage dans le completionHandler.

Méthode dans la sous-class UICollectionViewCell .

 - (void)setPhoto:(Photo *)photo withImage:(UIImage *)image { [self setBackgroundColor:[UIColor blackColor]]; [self.imageView setBackgroundColor:[UIColor clearColor]]; if (photo && !image) { [photo imageForFootageSize:[Footage footageSizeThatBestFitsRect:self.bounds] withCompletionHandler:^(UIImage *image) { if ([self.delegate respondsToSelector:@selector(galleryPhotoCollectionViewCell:didLoadImage:)]) { [self.delegate galleryPhotoCollectionViewCell:self didLoadImage:image]; } image = nil; }]; } [self.imageView setImage:image]; BOOL isPhotoAvailable = (BOOL)(image); [self.imageView setHidden:!isPhotoAvailable]; [self.activityIndicatorView setHidden:isPhotoAvailable]; } 

Méthode dans le UICollectionView la source de données UICollectionView

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { DIGalleryPhotoCollectionViewCell *photoCell = [collectionView dequeueReusableCellWithReuseIdentifier:photoCellIdentifier forIndexPath:indexPath]; [photoCell setDelegate:self]; Footage *footage = [self footageForIndexPath:indexPath]; Photo *photo = ([footage isKindOfClass:[Photo class]]) ? (Photo *)footage : nil; if (photo) { // // Photo // [photoCell setPhoto:photo withImage:[self.galleryCache objectForKey:photo.footageID]]; } return photoCell; } 

Voici les autres methods pertinentes:

 - (void)galleryPhotoCollectionViewCell:(DIGalleryPhotoCollectionViewCell *)cell didLoadImage:(UIImage *)image { NSIndexPath *indexPath = [self.galleryCollectionView indexPathForCell:cell]; Footage *footage = [self footageForIndexPath:indexPath]; if ([footage isKindOfClass:[Footage class]]) { Photo *photo = (Photo *)footage; UIImage *cachedImage = [self.galleryCache objectForKey:photo.footageID]; if (!cachedImage) { cachedImage = image; [self.galleryCache setObject:image forKey:photo.footageID]; } [cell setPhoto:photo withImage:image]; } } 

Et aussi ma méthode getter pour la propriété NSCache

 - (NSCache *)galleryCache { if (!_galleryCache) { _galleryCache = [[NSCache alloc] init]; } return _galleryCache; } 

MODIFIER

Voici un instantané des instruments montrant l'historique du count de retenue de l'un des NSCache une fois que son propriétaire (un controller de vue) est fermé.

Je ne vois rien d'évident ici, bien que je suggère de mettre un point d'arrêt où vous purgez le cache et assurez-vous que cela se passe réellement comme vous le pensez.

Si vous ne le trouvez toujours pas, vous pouvez exécuter l'outil Allocations dans Instruments et activer le "nombre de references d'logging" (voir la dernière partie de cette réponse, application iOS avec ARC, find qui est le propriétaire d'un object ). Exactement où votre reference forte persistante est, à quel point vous pouvez aborder la remédiation.

L'autre solution évidente est d'éliminer tout ce code et d'utiliser un outil de caching d'image éprouvé, comme SDWebImage qui fait beaucoup de memory et de cache de stockage persistant pour vous. C'est une mise en œuvre assez décente.

OK, donc après avoir examiné mon propre code et ré examiner les propriétés pour le milliardième time xn, il s'avère que mon erreur assignait la propriété du délégué comme un type 'fort'. Leçon apprise: TOUJOURS définir les delegates comme FAIBLES.

Cependant, je vais certainement devoir en apprendre davantage sur les instruments.