滚动UICollectionView会使用大量内存,导致崩溃。

3

我正在开发一个表情符号键盘。我的方法如下:

  • I decided to use UICollectionView. I do everything in code and don't intend to use Xib files.
  • I create a subclass of UICollectionViewCell. This is going to contain a single label showing the Emoji. This is what I do inside its initWithFrame

    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if (self) {
            if (_label == nil) {
            _label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
    
            _label.autoresizingMask = (UIViewAutoresizingFlexibleWidth |
                                       UIViewAutoresizingFlexibleHeight);
    
            _label.textAlignment = NSTextAlignmentCenter;
            [_label setNumberOfLines:1];
            self.contentView.layer.cornerRadius = 6.0;
    
            [self.contentView addSubview:_label];
            }
        }
    
        return self;
    }
    
  • In UICollectionView dataSource object, I read a plist file containing an NSDictionary with NSString as keys and NSArrays as values. Inside each NSArray, one can find the emojis I'm going to show. I then store the dictionary in a property. Here is the code:

    @property (nonatomic, strong) NSDictionary *emojis;
    
    - (NSDictionary *)emojis {
         if (!_emojis) {
              NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"EmojisList"
                                                          ofType:@"plist"];
             _emojis = [NSDictionary dictionaryWithContentsOfFile:plistPath];
        }
        return _emojis;
    }
    
  • In the following method, I try to populate the cells:

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
            NSString *cellIdentifier = @"Cell";
    
            EmojiCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
            cell.label.font = [UIFont systemFontOfSize:self.isiPad ? 47 : 33];
    
            NSArray *dataArray = [self.emojis objectForKey:self.categoryNames[indexPath.section]];
            cell.label.text = dataArray[indexPath.row];
    
            return cell;
    }
    

我的问题是,当我滚动时,内存使用量会增加。这导致真实设备上崩溃。

请帮助我。我尝试了许多不同的解决方法,但都没有成功。

这是仪器截图。我真的不知道这些是关于什么的。

滚动时内存使用量增加


你需要运行Instruments工具来查找代码中哪部分存在内存泄漏问题。请参考:http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode - Richard Stelling
@rjstelling 我已经做了很多次了。这与我的方法无关。只有一些与CFString相关的方法。正如我在下面的评论中所说,我发现当我将字体大小设置为较高值时,内存使用量会更多。这让我疯狂。 - sdtaheri
@mani 你好。我看到你帮助了一个有类似问题的人。能否请你帮帮我? - sdtaheri
6个回答

1

字体大小起到了关键作用。

“Apple Color Emoji”字体根据字体大小将表情符号替换为不同尺寸的PNG图像。更大的图像很快就会使用完40MB的内存限制。

在我的情况下,我尝试了16号字体大小,并使用了1.5倍的比例变换使其变得足够大。结果看起来不是很好,但至少它能工作...


很好的解释。那么苹果是如何为自己的键盘做到这一点的呢?特别是在iPad上。它们非常清晰。像SwiftKey这样的键盘也可以做到,而且不会崩溃。 - sdtaheri
@sdtaheri,我认为系统键盘的内存限制不会像第三方键盘那样低。我认为更好的解决方案是使用自己的图像,而不是使用带有特殊字体的表情符号字符。我稍后会尝试这种方法。 - Igotit
如果你这样做了,我很乐意知道结果。 - sdtaheri

0

我也遇到了同样的问题, 尽管问题仍然存在但是……

我发现使用 transform 而不是 UIFont.size 时,可以显著减少内存使用量。


是的,我已经尝试过了,我的键盘仍然使用高达30MB的内存来承载1200个表情符号图标。 - TomSawyer

0

我不确定有多少内存被真正分配了,但它不应该因为集合视图而崩溃。然而,请注意,键盘,特别是在iPhone 6+上运行时,受到相当小的内存占用限制。显示许多子视图的许多单元格可能会导致类似于这样的内存问题。

然而,我认为这是由于保留循环引起的。两个类最有可能强烈地捕获彼此。这可以发生在任何类型的块或两个相互引用的强属性中。

当您无法追踪发生这种情况的点时,最简单的方法可能就是缩小可能导致其发生的代码范围。例如,通过不从plist加载表情符号来检查该代码是否会受到影响。

希望这可以帮助您,但要完全了解您的整个项目,基本上是不可能的。


0
你每次加载单元格时都从磁盘读取 plist 吗?如果是这样,那么你可能会在每次加载单元格时将 plist 读入内存中,并且它没有被释放。尝试禁用读取 plist 的代码(现在将一些测试字符串放入数组中),看看是否有帮助。如果是这样,那就是问题所在。

当然不是!看第二段代码片段。只有在_emojis为空时,我才从plist文件中读取。我还删除了从plist文件读取的部分,并在代码中加载了字典。但这也没有帮助。 - sdtaheri

0

当你滚动屏幕时,你可能想要将UIViewCells和内容卸载出内存,因为这些单元格已经离开了屏幕。UICollectionView应该会自动处理,但最好再次确认一下。


当它被出队时,这不应该自动完成吗?我发现了一些相当奇怪的事情。字体大小越大,它使用的内存就越多! - sdtaheri
你需要哪个具体的部分? - sdtaheri

-1
尝试将标签的不透明度设置为YES。

使用审核工具检查了 Snapchat 的用户界面,并将其标签的不透明度设置为 YES。 - lieyunye

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接