我正在为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()
}
后,我得到了这样的内存使用情况:
我遇到了同样的问题并通过从/ 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页,并释放其他看不见的页面。
我有同样的问题并尝试了很多东西来释放记忆,但没有运气。我刚刚根据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)
}
我猜你正在使用func getImageFileNo(s: String) -> Int
{
if Array(emo.keys).contains(s)
{
return emo[s]!
}
return -1
}
加载图像,或者是从它衍生的东西。这将缓存系统缓存中的图像。
您需要使用[UIImage imageNamed:]
加载它们。这将绕过缓存。
(如果那不是问题,那么你需要在你的问题中包含一些代码,以便我们可以看到正在发生的事情。)
许多表情符号由[UIImage imageWithContentsOfFile:]
代表,其中包含多个unicode标量。 sequences适用于基本的表情符号,但它只返回表情符号序列中的第一个标量,如国旗。
下面的代码将获得完整的序列并创建一个匹配Matthew's answer导出的文件名的字符串。
一些简单的笑脸表情符号也有gemoji选择器。但gemoji不会在导出时将此选择器添加到文件名,因此应将其删除。
fe0f
我也一直在这个房子周围,经过多次测试我得出以下结论:
虽然字体缓存确实有助于扩展的内存占用量以及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
}
相比,这种方法的另一个问题是,除了字体缓存正在执行的操作外,更难以使用内存报告的总内存占用来仔细检查应用程序。也许有办法将这些分开,但我不知道。