NSCache拥有使用imageWithData实例化的UIImage的强指针,并且在卸载时不会从内存中删除

我有一个带有属性galleryCache的View Controller,当使用GCD和imageWithData:下载图像时:使用密钥成功地将图像添加到缓存中。 但是,当视图控制器被解除时,它会保留指向那些下载图像的强指针,导致它们不能从内存中删除。 即使我在viewDidDisappear:的缓存上使用removeAllObjects方法viewDidDisappear:内存也不会清除。

有谁知道为什么会这样?

以下是下载图像的方法的代码。

 - (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 attributes:nil error:nil]; // Save photo // NSString *localPath = [[self localURLForFootageSize:footageSize] path]; [imageData writeToFile:localPath atomically:YES]; } completionBlock(image); }); }); } }); }); } } 

使用上述类方法在completionHandler中获取和处理UIImage的方法。

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

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; } 

以下是其他相关方法:

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

还有我的NSCache属性galleryCache getter方法

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

编辑

以下是Instruments的快照,显示其中一个NSCache的所有者(视图控制器)被关闭后的保留计数历史记录。

我在这里看不到任何明显的东西,尽管我建议在你清除缓存的地方设置一个断点,并确保实际发生的事情就像你认为的那样。

如果你仍然没有找到它,你可以在Instruments中运行Allocations工具并打开“记录引用计数”(参见本答案的后半部分, 带有ARC的iOS应用程序,找到谁是对象的所有者 ),你可以找到准确地说出你的长期强烈参考,在这一点上你可以解决补救问题。

另一个明显的解决方案是消除所有这些代码,并使用经过validation的图像缓存工具,如SDWebImage ,它为您完成大量内存和持久存储缓存。 这是一个相当不错的实现。

好的,所以在重新检查我自己的代码并重新检查第十亿次xn时间的属性之后,事实certificate我的错误是将委托属性指定为“强”类型。 获得的经验教训:总是将代表设置为WEAK。

但是,我肯定要了解有关乐器的更多信息。