如何清除填充表情符号字符的字体缓存?

问题描述 投票:21回答:5

我正在为iPhone开发键盘扩展。有一个类似于Apple自己的表情符号键盘的表情符号屏幕,在qazxsw poi中显示了大约800个表情符号字符。

当滚动此表情符号UICollectionView时,内存使用量会增加而不会下降。我正在重复使用单元格,当使用显示800次的单个表情符号字符进行测试时,内存在滚动期间不会增加。

使用工具我发现我的代码中没有内存泄漏,但似乎表情符号字形被缓存,并且根据字体大小可以占用大约10-30MB的内存(研究显示它们实际上是PNG)。键盘扩展可以在被杀之前使用很少的内存。有没有办法清除字体缓存?


编辑

添加代码示例以重现问题:

UIScrollView

在运行并滚动let data = Array("😄😊☺️😉😍😘😚😗😙😜😝😛😳😁😔😌😒😞😣😢😂😭😪😥😰😅😓😩😫😨😱😠😡😤😖😆😋😷😎😴😵😲😟😦😧😈👿😮😬😐😕😯😶😇😏😑👲👳👮👷💂👶👦👧👨👩👴👵👱👼👸😺😸😻😽😼🙀😿😹😾👹👺🙈🙉🙊💀👽💩🔥✨🌟💫💥💢💦💧💤💨👂👀👃👅👄👍👎👌👊✊✌️👋✋👐👆👇👉👈🙌🙏☝️👏💪🚶🏃💃👫👪👬👭💏💑👯🙆🙅💁🙋💆💇💅👰🙎🙍🙇🐶🐺🐱🐭🐹🐰🐸🐯🐨🐻🐷🐽🐮🐗🐵🐒🐴🐑🐘🐼🐧🐦🐤🐥🐣🐔🐍🐢🐛🐝🐜🐞🐌🐙🐚🐠🐟🐬🐳🐋🐄🐏🐀🐃🐅🐇🐉🐎🐐🐓🐕🐖🐁🐂🐲🐡🐊🐫🐪🐆🐈🐩🐾💐🌸🌷🍀🌹🌻🌺🍁🍃🍂🌿🌾🍄🌵🌴🌲🌳🌰🌱🌼🌐🌞🌝🌚🌑🌒🌓🌔🌕🌖🌗🌘🌜🌛🌙🌍🌎🌏🌋🌌🌠⭐️☀️⛅️☁️⚡️☔️❄️⛄️🌀🌁🌈🌊☕️🍵🍶🍼🍺🍻🍸🍹🍷🍴🍕🍔🍟🍗🍖🍝🍛🍤🍱🍣🍥🍙🍘🍚🍜🍲🍢🍡🍳🍞🍩🍮🍦🍨🍧🎂🍰🍪🍫🍬🍭🍯🍎🍏🍊🍋🍒🍇🍉🍓🍑🍈🍌🍐🍍🍠🍆🍅🌽🎍💝🎎🎒🎓🎏🎆🎇🎐🎑🎃👻🎅🎄🎁🎋🎉🎊🎈🎌🔮💛💙💜💚❤️💔💗💓💕💖💞💘💌💋💍💎👑👒👟👞👡👠👢👕👔👚👗🎽👖👘👙💼👜👝👛👓🎀🌂💄📚📖🔬🔭📰🎨🎬🎩🎪🎭🎤🎧🎼🎵🎶🎹🎻🎺🎷🎸👾🎮🃏🎴🀄️🎲🎯🏈🏀⚽️⚾️🎾🎱🏉🎳⛳️🚵🚴🏁🏇🏆🎿🏂🏊🏄🎣").map {String($0)} class CollectionViewTestController: UICollectionViewController { override func viewDidLoad() { collectionView?.registerClass(Cell.self, forCellWithReuseIdentifier: cellId) } override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return data.count } override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellId, forIndexPath: indexPath) as! Cell if cell.label.superview == nil { cell.label.frame = cell.contentView.bounds cell.contentView.addSubview(cell.label) cell.label.font = UIFont.systemFontOfSize(34) } cell.label.text = data[indexPath.item] return cell } override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { return 1 } } class Cell: UICollectionViewCell { private let label = UILabel() } 后,我得到了这样的内存使用情况:

ios caching memory-management fonts emoji
5个回答
10
投票

我遇到了同样的问题并通过从/ System / Library / Fonts / Apple Color Emoji.ttf转储.png并使用UIImage(contentsOfFile:String)而不是String来修复它。

我用UICollectionView提取.png文件,用@ 3x后缀重命名文件。

https://github.com/github/gemoji

UIImage(contentsOfFile:path!)被正确释放,因此内存应该保持在低水平。到目前为止,我的键盘扩展尚未崩溃。

如果UIScrollView包含大量表情符号,请考虑使用UICollectionView,它只在缓存中保留3或4页,并释放其他看不见的页面。


3
投票

我有同样的问题并尝试了很多东西来释放记忆,但没有运气。我刚刚根据Matthew的建议更改了代码。它有效,对我来说没有更多的内存问题,包括iPhone 6 Plus。

代码更改很少。在下面的UILabel子类中找到更改。如果你问我挑战是获得表情符号图像。我无法想象gemoji(func emojiToHex(emoji: String) -> String { let data = emoji.dataUsingEncoding(NSUTF32LittleEndianStringEncoding) var unicode: UInt32 = 0 data!.getBytes(&unicode, length:sizeof(UInt32)) return NSString(format: "%x", unicode) as! String } let path = NSBundle.mainBundle().pathForResource(emojiToHex(char) + "@3x", ofType: "png") UIImage(contentsOfFile: path!) )是如何工作的。

https://github.com/github/gemoji

emojiToHex()方法Matthew为某些emojis提供了以“/”开头的奇怪值。到目前为止,给定链接的解决方案没有问题。 //self.text = title //what it used to be let hex = emojiToHex(title) // this is not the one Matthew provides. That one return strange values starting with "/" for some emojis. let bundlePath = NSBundle.mainBundle().pathForResource(hex, ofType: "png") // if you don't happened to have the image if bundlePath == nil { self.text = title return } // if you do have the image else { var image = UIImage(contentsOfFile: bundlePath!) //(In my case source images 64 x 64 px) showing it with scale 2 is pretty much same as showing the emoji with font size 32. var cgImage = image!.CGImage image = UIImage( CGImage : cgImage, scale : 2, orientation: UIImageOrientation.Up )! let imageV = UIImageView(image : image) //center let x = (self.bounds.width - imageV.bounds.width) / 2 let y = (self.bounds.height - imageV.bounds.height) / 2 imageV.frame = CGRectMake( x, y, imageV.bounds.width, imageV.bounds.height) self.addSubview(imageV) }

Convert emoji to hex value using Swift

- - - - - 一段时间后 - -

事实证明,这种emojiToHex方法并不适用于每个表情符号。因此,我最终通过gemoji下载所有表情符号,并将每个表情符号图像文件(文件名如1.png,2.png等)与表情符号本身映射到字典对象中。现在改用以下方法。

func emojiToHex(emoji: String) -> String
{
    let uni = emoji.unicodeScalars // Unicode scalar values of the string
    let unicode = uni[uni.startIndex].value // First element as an UInt32

    return String(unicode, radix: 16, uppercase: true)
}

1
投票

我猜你正在使用func getImageFileNo(s: String) -> Int { if Array(emo.keys).contains(s) { return emo[s]! } return -1 } 加载图像,或者是从它衍生的东西。这将缓存系统缓存中的图像。

您需要使用[UIImage imageNamed:]加载它们。这将绕过缓存。

(如果那不是问题,那么你需要在你的问题中包含一些代码,以便我们可以看到正在发生的事情。)


0
投票

许多表情符号由[UIImage imageWithContentsOfFile:]代表,其中包含多个unicode标量。 sequences适用于基本的表情符号,但它只返回表情符号序列中的第一个标量,如国旗。

下面的代码将获得完整的序列并创建一个匹配Matthew's answer导出的文件名的字符串。

一些简单的笑脸表情符号也有gemoji选择器。但gemoji不会在导出时将此选择器添加到文件名,因此应将其删除。

fe0f

0
投票

我也一直在这个房子周围,经过多次测试我得出以下结论:

虽然字体缓存确实有助于扩展的内存占用量以及Xcode调试导航器和内存报告中的总使用量,但它的处理方式与预算的其余部分完全不同。

有些人引用50 MB作为扩展限制,而在Apple文档中,我认为我已经看过30或32 MB引用。我们在30到40 MB之间的各个点看到内存警告,这对于任何特定值都不太满意,但有一件事似乎具体是内存异常,发生在53 MB,这是由Xcode正好是那个数字。如果我使用空白键盘扩展并用甚至40 MB的图像视图填充它,这是一回事,但如果我使用30 MB然后使用20 MB的字体字形,我发现我的键盘没有关闭。

根据我的观察,字体缓存看起来得到了清理,但并不像你认为必要的那样频繁(特别是如果你在无用的组合内存值超过30或32 MB时变得紧张)。

如果你预算自己的内存使用量,比如30 MB,你应该是安全的,前提是你没有引入一个场景,其中一次性需要23 MB(即53-30)的字体字形。这将受到表情符号网格密度的影响,甚至可能影响使用的字体大小。这里常见的理解是,如果你要从表情符号集合视图的一端滚动到另一端,你将传递超过23 MB的字体字形,但是如果剩余的内存占用是合理的(即30 MB)或者以下),字体缓存应该有机会清理。

在我的测试中,我尝试使用更多的字体字形自动轰炸扩展,我认为我能够击败字体缓存清理过程,导致崩溃。

因此,考虑到UICollectionView的用例及其滚动速度,如果您真的推动了30 MB的内存预算并且滚动速度非常快,则可能会使应用程序崩溃。您可能允许自己达到这个53 MB的硬限制。

鉴于以上所有 - 具有完全成熟的键盘扩展,只要我保持大约30 MB我自己的(非字体字形)足迹我没有遇到崩溃,即使快速更改表情符号类别和快速滚动。但是,我确实以这种方式遇到了系统内存警告,这对我来说再次引起了怀疑。

与使用func emojiToHex(_ emoji: String) -> String { var name = "" for item in emoji.unicodeScalars { name += String(item.value, radix: 16, uppercase: false) if item != emoji.unicodeScalars.last { name += "-" } } name = name.replacingOccurrences(of: "-fe0f", with: "") return name } 相比,这种方法的另一个问题是,除了字体缓存正在执行的操作外,更难以使用内存报告的总内存占用来仔细检查应用程序。也许有办法将这些分开,但我不知道。

© www.soinside.com 2019 - 2024. All rights reserved.