How to clear font cache filled with emoji characte

2019-01-22 07:40发布

I am developing keyboard extension for iPhone. There is an emoji screen smilar to Apples own emoji keyboard that shows some 800 emoji characters in UICollectionView.

When this emoji UIScrollView is scrolled the memory usage increases and does not drop down. I am reusing cells correctly and when testing with single emoji character displayed 800 times the memory does not increase during scrolling.

Using instruments I found that there is no memory leak in my code but it seems that the emoji glyphs are cached and can take around 10-30MB of memory depending on font size (reseach shows they are actually PNGs). Keyboard extensions can use little memory before they are killed. Is there a way to clear that font cache?


Edit

Adding code example to reproduce the problem:

let data = Array("                

4条回答
放荡不羁爱自由
2楼-- · 2019-01-22 08:03

Many emojis are represented by sequences that contain more than one unicode scalar. Matthew's answer works well with basic emojis but it returns only first scalar from the sequences of emojis like country flags.

The code below will get full sequences and create a string that matches gemoji exported file names.

Some simple smiley emojis also have the fe0f selector. But gemoji doesn't add this selector to file names on exporting, so it should be removed.

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
}
查看更多
Ridiculous、
3楼-- · 2019-01-22 08:07

I had the same issue and tried many things to release the memory, but no luck. I just changed the code based on Matthew's suggestion. It works, no more memory problem for me including iPhone 6 Plus.

The code change is minimal. Find the change in the UILabel subclass below. If you ask me the challenge is to get the emoji images. I could not figure how gemoji (https://github.com/github/gemoji) works out yet.

    //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)
    }

The emojiToHex() method Matthew provides returns strange values starting with "/" for some emojis. The solution at the given link work with no problems so far. Convert emoji to hex value using Swift

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)
}

---------- AFTER SOME TIME----

It turned out this emojiToHex method does not work for every emoji. So I end up downloading all emojis by gemoji and map each and every emoji image file (file names are like 1.png, 2.png, etc) with the emoji itself in a dictionary object. Using the following method instead now.

func getImageFileNo(s: String) -> Int
{
        if Array(emo.keys).contains(s)
        {
             return emo[s]!
        }   
        return -1
}
查看更多
相关推荐>>
4楼-- · 2019-01-22 08:09

I am guessing that you are loading the images using [UIImage imageNamed:], or something that derives from it. That will cache the images in the system cache.

You need to load them using [UIImage imageWithContentsOfFile:] instead. That will bypass the cache.

(And if that's not the problem, then you'll need to include some code in your question so that we can see what's happening.)

查看更多
可以哭但决不认输i
5楼-- · 2019-01-22 08:12

I ran into the same issue and fixed it by dumping the .png from /System/Library/Fonts/Apple Color Emoji.ttf and using UIImage(contentsOfFile: String) instead of a String.

I used https://github.com/github/gemoji to extract the .png files, renamed the files with @3x suffix.

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!)

UIImage(contentsOfFile: path!) is properly released so the memory should stay at a low level. So far my keyboard extension hasn't crashed yet.

If the UIScrollView contains a lot of emoji, consider using UICollectionView that retains only 3 or 4 pages in cache and releases the other unseen pages.

查看更多
登录 后发表回答