2

我正在通过练习将一些数据存储在 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];
    }    
}
4

2 回答 2

1

问题不在您的 userdefaults 上,而是在您的 mutableArray 上,您在其中初始化、插入和删除对象。请检查你的可变数组对象,如果它被释放,那么你尝试访问它肯定会崩溃

于 2013-10-26T06:28:36.020 回答
0

首先感谢 Brad Allred 的提示。

让我失望的是,如果我禁用了这条线:

[[NSUserDefaults standardUserDefaults] setObject:recentPhotoList forKey:RECENT_PHOTO_LIST];

然后泄漏消失了,但那是解决症状而不是根本原因。

我的 NSDictionary 的 setter 方法调用了 addPhotoToRecentList() 方法:

- (void)setSelectedPhotoDict: givenSelectedPhotoDict

根据布拉德的建议,我重新检查了该物业:

@property (nonatomic, retain) NSDictionary *selectedPhotoDict;

这让我想到,在 setter 函数中,我有一行:

_selectedPhotoDict = [givenSelectedPhotoDict retain];

所以我删除了保留:_selectedPhotoDict = givenSelectedPhotoDict;

运行分析,一切都很干净,使用同一组测试用例重新运行 Profiler 几次,泄漏就消失了。

于 2013-10-26T05:57:34.900 回答