3

我正在添加文本UIlabel,以及它的性能成本(我使用此链接使用了构建时间分析器)。我该如何优化这段代码?

for value in model?.offerings ?? [] {
    offeringsLabel.text = offeringsLabel.text! + " | " +  (value.name ?? "") + "," +  (value.type ?? "")//this addition cost to performance
}

我也试过[ array].joined,但这并没有什么不同

4

5 回答 5

7

首先,对于根本问题。为什么慢?根据我的经验,链接+是导致大量编译时性能问题的最常见原因。这是因为+有很多重载(我在 4.2 的 stdlib 中计算了 103 个)。Swift 不仅要证明它+可以(String, String)按照你的意愿使用。它必须证明没有其他可能的重载组合同样有效(如果有,它将是模棱两可的并且是错误的)。这不仅包括简单的情况,如 (Int, Int)。它还包括适用于 (String, String) 的复杂的基于协议的重载,但不如 (String, String) 精确,例如:

static func + <Other>(lhs: String, rhs: Other) -> String 
    where Other : RangeReplaceableCollection, Character == Other.Element

这需要很长时间,因为当您将一堆+链接在一起时,它具有组合爆炸性。每个子表达式都必须根据它可能返回的所有内容重新考虑。

如何解决?首先,在这种情况下,我不会使用for循环来逐个构建字符串。只是map每个Offering你想要的字符串,然后将它们连接在一起:

offeringsLabel.text = model?.offerings
    .map { "\($0.name ?? ""),\($0.type ?? "")" }
    .joined(separator: " | ")

这不仅会编译得更快,而且 IMO 更清楚你在做什么。它也不需要 a !,这很好。(还有其他方法可以解决这个问题,但这是一个很好的副作用)。

也就是说,这也表明您的模型中可能存在问题。这是一个单独的问题,但我仍然会认真对待。任何时候你的代码看起来像:

optionalString ?? ""

您需要问,这真的应该是可选的吗?你真的对待零名称与空名称不同吗?如果不是,我相信nameandtype应该只是String而不是String?,然后这变得更加简单。

offeringsLabel.text = model?.offerings
    .map { "\($0.name),\($0.type)" }
    .joined(separator: " | ")

此代码与您的代码略有不同。When modelis nil,您的代码不会受到offeringsLabel影响。我的代码清除了文本。这就提出了一个更深层次的问题:为什么在modelnil 时运行这段代码?为什么是model可选的?您是否可以在数据中将其设为非可选,或者您是否应该guard let model = model else { return }在此方法中使用更早的方法?

根据我的经验,导致 Swift 过于复杂的常见原因是过度使用 Optional。Optional 非常重要和强大,但你不应该使用它,除非你真正的意思是“这里没有任何价值是合法的,并且与'空'不同”。

于 2018-11-30T20:36:19.657 回答
2

我的建议是首先descriptionOffering对象中添加一个属性以正确处理(您的解决方案始终在namevalue之间放置一个逗号namevalue无论是否name有值)

var description : String? {
    let desc = [name, value].compactMap{$0}.joined(separator:",")
    return desc.isEmpty ? nil : desc
}

而不是循环使用compactMapandjoined

offeringsLabel.text = model?.offerings?.compactMap { $0.description }.joined(separator:" | ") ?? ""
于 2018-11-30T12:56:14.590 回答
1

而不是UILabel在每次迭代中分配一个文本并在下一个迭代中再次读取它,您可以使用Array.reduce首先获取完整的字符串

let fullString = (model?.offerings ?? []).reduce("", { string, value in
    string + " | " +  (value.name ?? "") + "," +  (value.type ?? "")
}
offeringsLabel.text = fullString

重复设置text会影响性能,因为例如,它可以触发动态大小标签的大小重新计算

于 2018-11-30T11:48:01.470 回答
0

你应该在这里使用临时变量。?? 如果在复杂表达式中使用运算符,可能会显着增加编译时间

所以你可以用以下更新你的代码(是的,它不短,但我们应该帮助编译器)

let offerings = model?.offerings ?? []
var offeringsText = ""
for value in offerings {
    let name = value.name ?? ""
    let type = value.type ?? ""
    let valueText = " | " +  name + "," +  type
    let offeringsText = offeringsText + valueText
}
offeringsLabel.text = offeringsText

希望对你有帮助!

于 2018-11-30T20:17:50.867 回答
0

您可以尝试附加功能,例如:

let valueName = value.name ?? ""
offeringsLabel.text?.append(valueName) 
于 2018-11-30T11:54:05.757 回答