iOS 带有自定义字体的今天小部件因内存错误而终止

iOS Today Widget with custom font terminated due to memory error

今日小工具

我们有一个使用自定义字体的 iOS 8+ 应用程序。 TTF 文件与应用程序捆绑在一起。

我们现在正在制作一个今天的小部件(扩展),它需要使用相同的自定义字体才能正确显示内容。

TTF 文件本身为 142 KB。

根据我的测试数据,使用自定义字体只会绘制 3-4 个字符。

症状

问题是使用这种字体会使小部件占用过多内存,并且小部件会调用一些 didReceiveMemoryWarning 然后终止。

如果我们改用 "HelveticaNeue" 字体,则一切正常,但有问题的字符是根据 HelveticaNeue 而不是自定义字体显示的。

在 iOS 模拟器中一切正常,但是几个 Whosebug questions/answers 让我相信这是很正常的,并且 iOS 内存保护机制只会在设备上启动.

在 iPhone 6 上,小部件被杀死,内存使用量约为 20 MB,因此我们不会在这里谈论太多。在 iPhone 4S 上,限制似乎更低。

问题

所以问题是是否有解决方法。或者一些 属性 可以调整或研究的自定义字体。

让主应用程序以某种方式将字体预呈现为磁盘上可以由小部件加载的内容(png 文件?)是否有意义?怎么做到的?

有任何关于方法或需要研究的建议吗?

我们未能找到减少字体所用内存的方法。

我们通过使用 ImageMagick 和 perl 脚本为我们正在使用的所有可能大小和字形生成 png 文件,成功地解决了这个问题。

png 文件放置在具有字体名称和磅值的目录结构中。每个字形都有自己的 png,以十六进制 Unicode 代码点命名。

然后将所有 png 文件放入 Xcode 项目,这样最终会出现在包中。

在 运行 时,我们不使用字体来渲染,而是使用 [UIImage imageNamed:] 并渲染它。

该解决方案运行良好,并且在小部件中使用的内存量有限。

有几个缺点:

  1. 小部件和主应用程序包变得臃肿:添加了 1000 多个 png 文件(575 KB = 4.7 MB 磁盘空间)。我们还不知道这会增加多少应用程序的分发大小。
  2. 我们失去了原始方法的灵活性。新的设备类型或对动态类型的更好支持可能会导致需要更多尺寸,然后我们需要生成更多 png。
  3. 更大的错误风险

生成 png 的脚本是:

#!/usr/bin/perl

use File::Path qw(make_path);

$fontName = "SSSymboliconsBlock";
@sizes = (10, 11, 13, 16, 18);

foreach $size (@sizes) {
    $path = "fontCache/$fontName/$size";
    make_path($path);
    @glyphs = ("1f50d", "1f512", "1f511");

    foreach $glyphNo (@glyphs) {
        $glyph = chr(hex($glyphNo));
        system("convert -background none -fill black -font $fontName.ttf -pointsize $size label:\"$glyph\" $path/$glyphNo.png");
    }
}

加载图像是这样的:

- (UIImage *)loadImageForGlyph:(NSString *)glyph
                      fontName:(NSString *)fontName
                      fontSize:(CGFloat)fontSize
{
    // NSDictionary that translates from a glyph to a string with the hex
    // value of the code point.
    // e.g. glyph = @"", codePoint = @"1f50d"
    NSString * codePoint = self.codePointByGlyph[glyph];
    NSString * path
    = [[[[@"fontCache" stringByAppendingPathComponent:fontName]
       stringByAppendingPathComponent:[@(fontSize) stringValue]]
        stringByAppendingPathComponent:codePoint]
       stringByAppendingPathExtension:@"png"];

    UIImage * img = [UIImage imageNamed:path];

    return img;
}