调用NSUserDefaults setObject时内存泄漏

时间:2013-10-26 00:56:35

标签: objective-c memory-leaks nsuserdefaults

我正在通过练习在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来保存,但是我想了解这个的来源记忆泄漏。

enter image description here

此功能的来电者是:

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

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

首先,谢谢Brad Allred的提示。

让我失望的是,如果我禁用该行:

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

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

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

- (void)setSelectedPhotoDict: givenSelectedPhotoDict

根据布拉德的建议,我重新检查了这个属性:

@property (nonatomic, retain) NSDictionary *selectedPhotoDict;

让我想到的是,在setter函数中,我有了这句话:

_selectedPhotoDict = [givenSelectedPhotoDict retain];

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

运行Analyze,一切都很干净,使用相同的测试用例重新运行Profiler几次,泄漏消失了。