我正在通过练习将一些数据存储在 NSUserDefaults 中。数据是字典对象的列表,其中包含关于每张图片的唯一标识信息。该列表大约有 20 个元素,字典中的信息是一堆小字符串。图像本身当然不会被存储。
我故意禁用了 ARC,以便我可以学习内存管理。
我运行了探查器,并且在将该数组存储回 NSUserDefaults 的行上遇到了内存泄漏。有任何想法吗?
+ (void) addPhotoToRecentList:(NSDictionary *)photoInformation
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *photoId = [photoInformation objectForKey:(FLICKR_PHOTO_ID)];
NSMutableArray *recentPhotoList = [NSMutableArray arrayWithArray:[defaults objectForKey:RECENT_PHOTO_LIST]];
if (!recentPhotoList) {
recentPhotoList = [[[NSMutableArray alloc] init] autorelease];
} else {
NSLog(@"recentPhotoList is %@", recentPhotoList);
NSDictionary *photoElement;
for (photoElement in recentPhotoList) {
if ([photoId isEqualToString:[photoElement objectForKey:(FLICKR_PHOTO_ID)]]) {
[recentPhotoList removeObject:photoElement];
break;
}
}
if (recentPhotoList.count == MAX_PHOTOS) {
[recentPhotoList removeLastObject];
}
}
[recentPhotoList insertObject:photoInformation atIndex:0];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:RECENT_PHOTO_LIST];
[[NSUserDefaults standardUserDefaults] setObject:recentPhotoList forKey:RECENT_PHOTO_LIST];
[[NSUserDefaults standardUserDefaults] synchronize];
}
请注意,我添加了以下行:
[[NSUserDefaults standardUserDefaults] removeObjectForKey:RECENT_PHOTO_LIST];
想知道这是否有助于解决内存泄漏,但这并没有什么区别。
如果我注释掉该行: [[NSUserDefaults standardUserDefaults] setObject:recentPhotoList forKey:RECENT_PHOTO_LIST];
然后没有更多的内存泄漏,但我也没有保存最近访问的图片列表。
这就是我有内存泄漏的事实,我用 Leaks Instrument 运行了 Profiler。此外,在我这样做之前,我运行了分析器并清理了所有有问题的问题。没有警告。
另外,我可能违反了一些 iOS 规则。我有两个单独的视图可以修改经常保存的列表。作为一个捷径,我决定将它保存在 NSUserDefaults 中,所以如果我经常点击图片列表,那么我每次都会调用保存到 NSUserDefaults。如果我调用最近查看列表控制器,它会从 NSUserDefaults 中提取该信息并更新它以将最近的照片保持在顶部。
我正在考虑创建一个可由这些视图访问的全局类以临时保存到列表中,然后当应用程序进入后台或退出时,它会调用 NSUserDefaults 进行保存,但是我想了解此内存泄漏的来源。
这个函数的调用者是:
- (void) doActualWork
{
if (!self.selectedPhotoDict) {
NSLog(@"AMRO No picture selected must be an iPad");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
return;
}
photoData = [[FlickrFetcher imageDataForPhotoWithFlickrInfo:self.selectedPhotoDict format:FlickrFetcherPhotoFormatLarge] retain];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[RecentTableViewController addPhotoToRecentList:self.selectedPhotoDict];
//With Splitviews this is how we refresh our view
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
[self loadPhoto];
}
}
调用者来自另一个视图,它推动照片视图控制器加载特定图片。我在后台运行该功能以允许我将网络指示器设置为忙。
- (void)setSelectedPhotoDict:givenSelectedPhotoDict
{
if (_selectedPhotoDict) {
[_selectedPhotoDict release];
}
_selectedPhotoDict = [givenSelectedPhotoDict retain];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
[self.imageView setNeedsDisplay]; // any time our Model changes, redraw our View
if (photoData) {
[photoData release];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self performSelector:@selector(doActualWork) withObject:Nil afterDelay:0.01];
}
}