Memory management UICollectionView中的内存警告(带图像)

Memory management UICollectionView中的内存警告(带图像),memory-management,afnetworking,uicollectionview,Memory Management,Afnetworking,Uicollectionview,我目前正在开发一个包含大量图像的UICollectionView。但是,它有时会在此视图中崩溃,并伴有内存警告。我使用AFNetworking和UIImageView+AFNetworking类别通过setImageWithURL:方法设置图像。一个问题可能是缓存。我不确定AFNetworking是否处理图像缓存。无论如何,有没有一种方法可以在内存管理方面优化这段代码?或者,如果我要在这个视图控制器中实现didReceiveMemoryWarning方法,这个方法中可以放什么?我附加此集合视图的

我目前正在开发一个包含大量图像的UICollectionView。但是,它有时会在此视图中崩溃,并伴有内存警告。我使用AFNetworking和UIImageView+AFNetworking类别通过setImageWithURL:方法设置图像。一个问题可能是缓存。我不确定AFNetworking是否处理图像缓存。无论如何,有没有一种方法可以在内存管理方面优化这段代码?或者,如果我要在这个视图控制器中实现didReceiveMemoryWarning方法,这个方法中可以放什么?我附加此集合视图的cellForItemAtIndexPath代码

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"RecipeCell" forIndexPath:indexPath];

// setting the image view for the cell using AFNetworking. Does this do caching automatically?
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:6];
if (PRODUCTION) {
    [recipeImageView setImageWithURL:[[self.recipes objectAtIndex:indexPath.row] objectForKey:@"recipe_image"] placeholderImage:[UIImage imageNamed:@"default_recipe_picture.png"]];
} else {
    [recipeImageView setImageWithURL:[NSString stringWithFormat:@"http://localhost:5000/%@", [[self.recipes objectAtIndex:indexPath.row] objectForKey:@"recipe_image"]] placeholderImage:[UIImage imageNamed:@"default_recipe_picture.png"]];
}


// configure the back of the cell. fill all the info.
UITextView *recipeNameView = (UITextView *)[cell viewWithTag:8];
recipeNameView.text = [NSString stringWithFormat:@"%@", [[self.recipes objectAtIndex:indexPath.row] objectForKey:@"recipe_name"]];

UILabel *recipeNameLabel = (UILabel *)[cell viewWithTag:2];
recipeNameLabel.text = [NSString stringWithFormat:@"%@", [[self.recipes objectAtIndex:indexPath.row] objectForKey:@"recipe_name"]];

NSDictionary *user = [[self.recipes objectAtIndex:indexPath.row] objectForKey:@"user"];
UIButton *chefNameButton = (UIButton *)[cell viewWithTag:3];
[chefNameButton setTitle:[NSString stringWithFormat:@"%@ %@", [user objectForKey:@"first_name"], [user objectForKey:@"last_name"]] forState:UIControlStateNormal];

NSMutableArray *missingIngredientsStringArray = [[NSMutableArray alloc] init];
NSArray *missingIngredients = [[self.recipes objectAtIndex:indexPath.row] objectForKey:@"missing_ingredients"];
for (NSDictionary *missingIngredient in missingIngredients) {
    [missingIngredientsStringArray addObject:[missingIngredient objectForKey:@"name"]];
}
NSString *missingIngredientsString = [missingIngredientsStringArray componentsJoinedByString:@","];

UITextView *missingIngredientsView = (UITextView *)[cell viewWithTag:4];
missingIngredientsView.text = [NSString stringWithFormat:@"%u Missing Ingredients: %@", missingIngredients.count, missingIngredientsString];

// configure the front of the cell. chef name button and missing ingredients and likes on front view
UIButton *frontNameButton = (UIButton *)[cell viewWithTag:11];
[frontNameButton setTitle:[NSString stringWithFormat:@"%@ %@", [user objectForKey:@"first_name"], [user objectForKey:@"last_name"]] forState:UIControlStateNormal];
[frontNameButton sizeToFit];
frontNameButton.frame = CGRectMake(160 - [frontNameButton.titleLabel.text sizeWithFont:[UIFont boldSystemFontOfSize:13]].width - 7, frontNameButton.frame.origin.y, frontNameButton.frame.size.width, frontNameButton.frame.size.height);

UILabel *likesLabel = (UILabel *)[cell viewWithTag:9];
likesLabel.text = [NSString stringWithFormat:@"%@ likes", [[self.recipes objectAtIndex:indexPath.row] objectForKey:@"likes"]];

UIButton *missingIngredientsButton = (UIButton *)[cell viewWithTag:12];
[missingIngredientsButton setBackgroundImage:[UIImage imageNamed:@"badge_green.png"] forState:UIControlStateSelected];
if (missingIngredients.count == 0) {
    missingIngredientsButton.selected = YES;
    [missingIngredientsButton setTitle:@"" forState:UIControlStateNormal];
} else {
    missingIngredientsButton.selected = NO;
    [missingIngredientsButton setTitle:[NSString stringWithFormat:@"%u", missingIngredients.count] forState:UIControlStateNormal];
}

// make back view invisible.
UIView *backView = [cell viewWithTag:1];
UIView *frontView = [cell viewWithTag:5];
frontView.alpha = 1.0;
backView.alpha = 0;

// adding flip gesture recognizers
UIView *flipView1 = [cell viewWithTag:12];
UIView *flipView2 = [cell viewWithTag:1];

UITapGestureRecognizer *flipGestureRecognizer1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(flipCell:)];
UITapGestureRecognizer *flipGestureRecognizer2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(flipCell:)];
[flipView1 addGestureRecognizer:flipGestureRecognizer1];
[flipView2 addGestureRecognizer:flipGestureRecognizer2];

return cell;
}
[编辑]我附上了我的仪器运行的屏幕截图。


您可以看到,当我反复按下segue和back按钮时,内存分配会增加。不断增长的是CFData、CALayer、CABackingStore和UITableView。我怀疑这些东西是在赛格之后创造的,它们不会被释放。。。请帮忙

AFNetworking自动将图像存储在
NSCache
集合中,该集合在内存不足警告时自动从内存中删除部分或全部图像。网络可能不是你的问题

事实上,我不认为显示图像是你的问题,除非你下载了很多非常大的图像并同时显示它们。(如果是这种情况,您应该尝试优化图像以在设备上显示,这样它们就不需要调整大小。)

我看到的一个问题是,每次单元格进入视图时,您都会向其添加手势识别器,但单元格是重复使用的,因此当单元格再次进入时,您会向其添加不必要的手势识别器。您可以通过将UITableViewCell子类化并将手势识别器指定为属性来解决此问题。您还可以通过选中
flipView1
flipView2
来解决此问题,以查看它们在添加之前是否连接了手势识别器。(但我不确定这是否足以引起内存警告。)

我建议使用Build->Profile并选择分配工具。在左侧,选择“仅目标C”,并隐藏系统调用。然后,滚动您的收藏视图并查看仪器,查看占用所有内存的内容

更新

以下是分配工具的屏幕截图:


您可能需要某种图像缓存策略,以避免重新下载图像。而且,
UIImageView+AFNetworking
category会为您缓存图像。但是,您也可以将响应缓存在内存中的URL缓存中,在本例中,这有点多余

<>你可以考虑减少或关闭内存中的URL缓存。我遇到了您描述的问题,以下内容大大减少了我的记忆问题:

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];

谢谢你的建议。尽管手势识别器对记忆不是那么重要,但这绝对是需要注意的。我正在对分配和泄漏进行分析,但我认为我无法从数据中提取有用的信息。我只是看到活动字节和总字节在增加,有时在打开搜索显示控制器时,malloc64字节会大幅增加。无论如何,我将再次尝试与目标C唯一的选项!我还有一个问题。在分配工具中,我在哪里选择仅目标C选项?在左边,我只看到堆快照分析、分配寿命、调用树、调用树约束和特定的数据挖掘。@HaraKang-用屏幕截图更新了我的答案。感谢您的建议。但有一件奇怪的事是,当我点击Show Obj-C only时,列表上什么也没有。然而,当我玩乐器时,我感觉不使用popViewControllerAnimated方法的back按钮并没有正确释放内存。。我还附上我的仪器结果。你能看看吗?例如,即使我按下后退按钮,UITableView的内存仍在不断增加。这是否意味着我在某个地方有一个保留周期?我将分析模式改为调试模式,现在它只显示obj-c列表!我发现大多数没有发布的东西都是从initWithCoder方法调用的,这是故事板的问题,对吧?谢谢你的建议。不过我有一个问题。共享URL缓存是否只处理图像?AFNetworking默认情况下不启用NSURLCache;仅当您创建一个时才启用它。而且,两个缓存都指向内存中相同的图像数据,所以我怀疑这会节省太多内存@harakang-see-it根据缓存策略缓存所有内容。“AFNetworking默认情况下不启用NSURLCache”据我所知(尽管我在这里不是专家),共享URL缓存是开箱即用的。AFNetworking不需要启用它@AaronBrager“同样,两个缓存都指向内存中相同的图像数据”这是一个很好的观点。实际上,为了实现更细粒度的控制(不缓存全尺寸图像+缓存解压缩的缩略图图像),我们放弃了
UIImageView+AFNetworking
。所以在我们的情况下,这很重要@AaronBrager@TimothyMoose-实际上我错了,它们指向内存中的相同数据
NSURLCache
缓存HTTP响应的数据
AFImageCache
缓存UIImage对象。因此,如果您有一个
NSURLCache
,您将缓存它两次。