为什么 UIFont(descriptor:size:) 的速度比 UIFont(name:size:) 慢 200 倍?

5

最近我注意到滚动的性能在减慢。我追踪了问题,发现原因是使用由UIFont(descriptor:size:)构造器创建的字体。我将该构造器更改为UIFont(name:size:),问题得到了解决。

我在一个项目中隔离了这个问题。代码如下:

import UIKit

class ViewController: UIViewController {
  @IBOutlet weak var firstLabel: UILabel!
  @IBOutlet weak var secondLabel: UILabel!

  @IBAction func onStartTest(sender: AnyObject) {
    startMeasurement()
    let firstFont = UIFont(name: "Marker Felt", size: 16)
    firstLabel.font = firstFont
    finishMeasurement("UIFont(name)")

    startMeasurement()
    let secondFontDescriptor = UIFontDescriptor(name: "Marker Felt", size: 16)
    let secondFont = UIFont(descriptor: secondFontDescriptor, size: 16)
    secondLabel.font = secondFont
    finishMeasurement("UIFont(descriptor)")
  }
}

private var time: UInt64 = 0

public func startMeasurement() {
  time = mach_absolute_time()
}

public func finishMeasurement(name: String) {
  let duration = mach_absolute_time() - time
  print("* \(name) \(duration)ns")
}

这些是我的一些测量结果:

iPhone 4S - iOS 9.0.2

* UIFont(name) 111,300ns
* UIFont(descriptor) 112,420,263ns

iPhone 6S - iOS iOS 9.2

* UIFont(name) 134,247ns
* UIFont(descriptor) 17,047,707ns

Simulator - iOS 9.2

* UIFont(name) 1,971,106ns
* UIFont(descriptor) 485,208,205ns

Simulator - iOS 8.1

* UIFont(name) 9,946,584ns
* UIFont(descriptor) 1,957,802,431ns

我做错了什么吗?


字体不应该影响滚动性能,除非您在某些滚动触发的回调中创建它。您可以提前创建字体并缓存它吗? - Noah Witherspoon
@NoahWitherspoon 当然,滚动问题已经解决了。但是文档没有提到UIFont(descriptor:size:)的性能问题,而调用UIFont(name:size:)的等效方法速度要快[125-200]倍。也许我使用UIFontDescriptor的方式不对。 - Alvivi
你找到答案了吗?我也遇到了这个问题,它正在破坏我的库的性能。只是将名称:大小交换增加了我的整体性能五倍! - Allison
1
@Sirens 不好意思。我记得在苹果论坛上问过一个苹果工程师,但没有回答。抱歉。 - Alvivi
1个回答

14

我在WWDC 2018向一位工程师咨询时,他告诉我:

描述符非常昂贵,因为它们每次都会搜索整个系统字体库以查找您提供的特征。他告诉我,这种搜索实际上并没有得到优化,因此不应在滚动情况下使用,并且实际上只应调用一次然后进行缓存。之所以这样做是因为您实际上不需要指定字体名称,而只需要指定其各种特性,系统就会为您提供相应字体。

UIFont(name: size:)非常快,因为它直接访问系统中请求的字体。它知道字体的位置,因为您已经给出了完整的确切名称,它只需按比例缩放即可。没有其他好的特点。

这大概是一个O(n)操作和一个O(1)操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接