【发布时间】:2016-03-22 22:13:46
【问题描述】:
我目前的任务是一个 iOS 键盘扩展,它提供所有 iOS 支持的表情符号(是的,我知道 iOS 有一个内置的表情符号键盘,但目标是在键盘扩展中包含一个)。
对于这个 Emoji 布局,它基本上应该是一个滚动视图,其中所有表情符号按网格顺序排列,我决定使用 UICollectionView,因为它只创建有限数量的单元格并重复使用它们。 (有相当多的表情符号,超过 1'000 个。)这些单元格只包含一个 UILabel,它将表情符号作为其文本保存,并带有一个 GestureRecognizer 以插入点击的 Emoji。
但是,当我滚动浏览列表时,我可以看到内存使用量从大约 16-18MB 增加到超过 33MB。虽然这不会在我的 iPhone 5s 上触发内存警告,但它也可能在其他设备上触发,因为应用扩展只占用了非常少量的资源。
编辑:有时我会收到内存警告,主要是在切换回“正常”键盘布局时。大多数情况下,切换回来时内存使用量会降至 20MB 以下,但并非总是如此。
如何减少此 Emoji 布局使用的内存量?
class EmojiView: UICollectionViewCell {
//...
override init(frame: CGRect) {
super.init(frame: frame)
self.userInteractionEnabled = true
let l = UILabel(frame: self.contentView.frame)
l.textAlignment = .Center
self.contentView.addSubview(l)
let tapper = UITapGestureRecognizer(target: self, action: "tap:")
self.addGestureRecognizer(tapper)
}
override func prepareForReuse() {
super.prepareForReuse()
//We know that there only is one subview of type UILabel
(self.contentView.subviews[0] as! UILabel).text = nil
}
}
//...
class EmojiViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
//...
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
//The reuse id "emojiCell" is registered in the view's init.
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("emojiCell", forIndexPath: indexPath)
//Get recently used emojis
if indexPath.section == 0 {
(cell.contentView.subviews[0] as! UILabel).text = recent.keys[recent.startIndex.advancedBy(indexPath.item)]
//Get emoji from full, hardcoded list
} else if indexPath.section == 1 {
(cell.contentView.subviews[0] as! UILabel).text = emojiList[indexPath.item]
}
return cell
}
//Two sections: recently used and complete list
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 2
}
}
let emojiList: [String] = [
"\u{1F600}",
"\u{1F601}",
"\u{1F602}",
//...
// I can't loop over a range, there are
// unused values and gaps in between.
]
如果您需要更多代码和/或信息,请告诉我。
编辑:我的猜测是 iOS 将渲染的表情符号保留在内存中的某个位置,尽管在重用之前将文本设置为 nil。但我可能完全错了......
编辑:按照 JasonNam 的建议,我使用 Xcode 的 Leaks 工具运行键盘。在那里我注意到了两件事:
-
VM: CoreAnimation在滚动时会达到大约 6-7MB,但我想这在滚动浏览集合视图时可能是正常的。 -
Malloc 16.00KB,从以千字节为单位的值开始,在滚动整个列表时会达到 17MB,因此分配了很多内存,但实际上我看不到其他任何东西使用它。
但没有报告泄漏。
EDIT2:我刚刚检查了CFGetRetainCount(在使用 ARC 时仍然有效),一旦设置了 prepareForReuse 中的 nil 值,String 对象就没有任何引用了。 p>
我正在使用 iOS 9.2 的 iPhone 5s 上进行测试,但问题也出现在使用 iPhone 6s Plus 的模拟器中。
EDIT3:有人遇到了完全相同的问题here,但由于标题奇怪,我到现在都没有找到。似乎唯一的解决方案是将 UIImageViews 与列表中的 UIImages 一起使用,因为 UICollectionView 中的 UIImages 在单元重用时会正确释放。
【问题讨论】:
-
您是否尝试过使用 Instruments 进行检查?您可以确定记忆的去向。
-
@JasonNam 请看我的编辑。
-
好吧实际上一千个 UILabel 可以容纳一些内存。您是否尝试将单元格的数量减少到 100 个?对内存使用有影响吗?
-
没有数以千计的 UILabel,就是这样:UICollectionViews(如果按上述方式实现)仅初始化将同时出现的子视图,然后通过更改内容重用它们(在这种情况下UILabel.text)。初始化的 UILabel 的实际数量是 56(调试输出)。并且内存使用量与滚动成正比。
-
啊哈,好吧,我只是提醒了重用单元格。好的,让我们看看
标签: ios iphone swift memory-management uicollectionview