为什么字符串加法需要这么长时间才能构建?

时间:2018-11-30 11:38:07

标签: ios arrays swift

我要在UIlabel中添加文本及其性能成本(我已经使用了构建时间分析器using this link)。我如何优化此代码?

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

我也尝试过[ array].joined,但这并没有什么区别

5 个答案:

答案 0 :(得分:5)

首先,提出基本问题。为什么这么慢?根据我的经验,链接+是导致大量编译时性能问题的最常见原因。这是因为+有很多重载(我在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更清楚了您在做什么。它还不需要!,这很好。 (还有其他方法可以解决此问题,但这是一个很好的副作用。)

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

optionalString ?? ""

您需要问,这真的是可选的吗?您真的将nil名称与空名称区别对待吗?如果没有,我相信nametype应该只是String而不是String?,然后变得更加简单。

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

此代码与您的代码略有不同。当modelnil时,您的代码将独自离开offeringsLabel。我的代码清除了文本。这就提出了一个更深层的问题:model为nil时为什么要运行此代码?为什么model是可选的?您可以使它在数据中成为非可选,还是在此方法的前面使用guard let model = model else { return }

根据我的经验,Swift过于复杂的常见原因是对Optional的过度使用。 Optional是非常重要且功能强大的,但是除非您真的表示“毫无价值是合法的,而且不同于'空',否则,您不应该使用它。”

答案 1 :(得分:2)

我的建议是首先在description对象中添加属性Offering,以正确处理namevalue(您的解决方案始终在{{1} }和name,无论value是否具有值)

name

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

而不是循环

joined

答案 2 :(得分:1)

您可以使用UILabel首先获取完整的字符串

,而不是在每次迭代中将文本分配给Array.reduce并在下一个迭代中再次读取文本
let fullString = (model?.offerings ?? []).reduce("", { string, value in
    string + " | " +  (value.name ?? "") + "," +  (value.type ?? "")
}
offeringsLabel.text = fullString

反复设置text会影响性能,因为例如,它可能会触发动态调整尺寸标签的尺寸重新计算

答案 3 :(得分:0)

您可以尝试添加函数,例如:

let valueName = value.name ?? ""
offeringsLabel.text?.append(valueName) 

答案 4 :(得分:0)

您应该在此处使用temp变量。 如果在复杂的表达式中使用运算符??,可能会大大增加编译时间

因此您可以使用以下代码更新代码(是的,它并不短,但是我们应该帮助编译)

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

希望这对您有帮助!