【问题标题】:Why string addition takes so long to build?为什么字符串添加需要这么长时间才能构建?
【发布时间】:2019-05-02 13:35:08
【问题描述】:

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

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

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

【问题讨论】:

  • 大部分是??——它在构建期间的类型推断检查存在一些问题。如需参考,请查看here
  • @user28434 你是对的。但是模型?.offerings 来自网络服务。 &它的可选。所以在 codable 中定义的任何解决方法?我正在使用 - struct Response:Codable { var offerings:[offerings]?
  • 字符串连接行中还有 2 个 ??。你只需要帮助类型推断,通过将带有?? 的sn-ps 提取到变量中,explicitly 提供你期望的类型。
  • 使用警卫,这样你就没有所有这些选项。然后它会表现得更好。

标签: ios arrays swift optimization compilation-time


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 这甚至无效Swift
    【解决方案3】:

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

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

    而不是循环使用compactMapjoined

    offeringsLabel.text = model?.offerings?.compactMap { $0.description }.joined(separator:" | ") ?? ""
    

    【讨论】:

    • 请注意,这会为 nil 名称或值返回与其他解决方案不同的值。 (这可能是好是坏,并​​且确实指出了 namevalue 首先几乎肯定不应该是可选的事实。)
    【解决方案4】:

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

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

    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
    

    希望对您有所帮助!

    【讨论】:

      【解决方案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 ?? ""
      

      您需要问,这真的应该是可选的吗?你真的对待零名称与空名称不同吗?如果不是,我认为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 非常重要和强大,但你不应该使用它,除非你真正的意思是“这里没有任何价值是合法的,并且与'空'不同。”

      【讨论】:

      • 非常感谢您为社区所做的努力。
      • “你真的对待 nil 名称与空名称不同吗” 这是一个很好的问题。不确定要使用什么措施来回答它。我能想到的只有一个是它是否改变了你的流程。例如如果你没有名字,那么不要实例化这个对象,或者不要显示这个标签。你能添加更多吗?
      • @honey 您必须有一个流程,其中没有名称将无法显示标签,但有一个空名称会显示标签。这将是一个非常令人惊讶的差异。然而,这种情况确实出现的情况是多层默认值。 “此设置设置为空”与“此设置(nil)继承其父级”之间存在差异。在这种情况下,String? 肯定是有道理的。
      • @RobNapier 你能帮忙吗 - stackoverflow.com/questions/55627683/…
      • 仅供参考,根据您的回答我更新了我的回答here 并比较了连接可选字符串和非可选字符串之间的编译时间。连接可选是超级慢,连接非可选似乎在使用+ 时根本没有任何影响。所以我有一个问题:说苹果已经解决了非可选的字符串连接是否正确,或者仍然存在影响编译时间的情况?
      猜你喜欢
      • 1970-01-01
      • 2019-11-13
      • 1970-01-01
      • 1970-01-01
      • 2012-04-29
      • 1970-01-01
      • 1970-01-01
      • 2020-02-15
      • 1970-01-01
      相关资源
      最近更新 更多