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

Posted

技术标签:

【中文标题】滚动 UICollectionView 使用大量内存导致崩溃【英文标题】:Scrolling UICollectionView uses lots of memory leading to crash 【发布时间】:2015-04-15 16:18:05 【问题描述】:

我正在开发表情符号键盘。这是我的方法:

我决定使用 UICollectionView。我用代码做所有事情,不打算使用 Xib 文件。

我创建了一个 UICollectionViewCell 的子类。这将包含一个显示表情符号的标签。这就是我在它的 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;

在 UICollectionView 数据源对象中,我读取了一个包含 NSDictionary 的 plist 文件,其中 NSString 作为键,NSArrays 作为值。在每个 NSArray 中,都可以找到我要展示的表情符号。然后我将字典存储在一个属性中。代码如下:

@property (nonatomic, strong) NSDictionary *emojis;

- (NSDictionary *)emojis 
     if (!_emojis) 
          NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"EmojisList"
                                                      ofType:@"plist"];
         _emojis = [NSDictionary dictionaryWithContentsOfFile:plistPath];
    
    return _emojis;

在以下方法中,我尝试填充单元格:

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

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

请帮助我。我测试了许多不同的方法来解决这个问题,但都没有成功。

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

【问题讨论】:

您需要运行工具来找出您的代码的哪一部分正在泄漏内存。见:raywenderlich.com/23037/how-to-use-instruments-in-xcode @rjstelling 我已经做过很多次了。这不是我的方法。只有一些与 CFString 或其他东西相关的方法。正如我在下面的评论中所说,当我将字体大小设置为更高的值时,我发现内存使用量更多。快把我逼疯了。 @mani 嗨。我看到你帮助了有类似问题的人。你能帮我解决这个问题吗? 【参考方案1】:

字体大小可以解决问题。

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

在我的例子中,我尝试了 16 的字体大小并使用 1.5 比例变换使其足够大。结果看起来不太好,但至少它可以工作......

【讨论】:

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

我不确定实际分配了多少内存,但它确实不应该因为集合视图而崩溃。但是,请务必意识到键盘,尤其是在 iPhone 6+ 上运行的键盘,仅限于相当小的内存占用。因此,显示带有许多子视图的大量单元格可能会导致类似的内存问题。

但是,我假设这是由于保留周期造成的。两个类最有可能强烈地相互捕捉。这可能发生在任何类型的块或两个相互引用的强属性中。

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

我希望这会有所帮助,如果不查看您的整个项目,基本上是无法判断的。

【讨论】:

【参考方案3】:

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

【讨论】:

当然不是!看第二个代码sn-p。我只在 _emojis 为 nil 时从 plist 文件中读取。我还删除了 plist 文件读取部分并在代码中加载了字典。它也没有帮助。【参考方案4】:

我也有同样的问题, 好吧,问题仍然存在,但是....

我发现,当我使用 transform 而不是 UIFont.size 时,它​​会大大减少内存使用量。

【讨论】:

是的,我试过了,我的键盘仍然使用高达 30mb 的内存来承载 1200 个表情符号 icns【参考方案5】:

尝试设置标签的不透明 YES

【讨论】:

用审阅工具检查了 snapchat UI,标签的不透明设置为 YES【参考方案6】:

您可能希望在滚动时从内存中卸载UIViewCells 和内容,并且这些单元格不在屏幕上。 UICollectionView 应该这样做,但我会仔细检查。

【讨论】:

不是应该在出队时自动完成吗?我发现了一些很奇怪的东西。字体越大,占用的内存越多!! 你到底需要什么零件?

以上是关于滚动 UICollectionView 使用大量内存导致崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Timer 在 UITableView 内自动滚动 UICollectionView?

如何防止使用 UICollectionView 滚动 UIScrollView

UICollectionView 滚动 ViewController

UICollectionView 不自动滚动

带有 pagingEnabled 的 UICollectionView 滚动动作控件

是否可以在第一列不动的情况下使 UICollectionView 视图水平滚动?