Swift和Objective-C之间的桥梁-我们为什么要关心?

介绍

自2014年首次面世以来,Swift取得了长足的发展。如今,大多数时候,您可以完全使用Swift编写应用程序。 但是,有时您会在Swift和Objective-C之间来回切换。 一个示例是使用NSAttributedString 。 这似乎很明显,无需担心。 但是,如果使用不当,可能会导致性能显着下降。

本文概述了在Swift和Objective-C之间桥接时发生的情况,并探讨了可能导致性能问题的一些潜在热点。

背景

导入Foundation ,您可以访问Objective-C开发人员使用了数十年的广泛的Objective-C类(带有NS前缀)。 其中许多类的行为(几乎)与等效的Swift构造相同。 例如, StringNSString ,以及ArrayNSArray

Swift和Objective-C是两种独立的语言,它们的实现,编译功能和运行时功能都非常不同。 他们如何相互理解,以及如何在这两种运行时表示形式之间进行转换? 通过所谓的桥接 。 尽管苹果公司已经尽可能快地优化了桥接,但是它从来都不是免费的。 在两种语言之间架桥时总要付出代价。

图1描述了两种语言之间的桥接时桥接的工作方式,我们分散设置了新的存储。 如果我们用一种语言处理很多事情,那么我们将用另一种语言处理很多事情。 我们必须逐个元素地进行转换。 这种逐元素桥接有时是递归的。 例如,如果我们有一个整数数组,则首先桥接该数组,然后桥接该数组中的每个单个整数。

识别桥接问题

桥接的常见示例是使用属性字符串时。 假设我们要实现一个自动完成的搜索文本视图,该视图允许用户键入一些文本以获取以用户键入的搜索词开头的建议列表。 建议将以粗体显示匹配的部分。 例如,如果用户输入“ ar ”,他们将看到一个弹出窗口,显示:

  • 阿根廷
  • 亚美尼亚
  • 阿鲁巴

样式化属性字符串的实现如下所示:

起初看起来很正常。 但是,让我们仔细看一下上面的代码片段。

  • 首先,我们从给定的String类型的String创建一个NSMutableAttributedString
  • 其次,我们使用属性字符串中的string属性,并询问要修饰的字符串的范围。 这可以确保它在目标字符串的范围内。 该请求返回Swift Range类型的值。
  • 第三,我们将上述步骤的结果转换为NSRange因为可变字符串仅适用于NSRange 。 注意,在此步骤中,我们再次从属性字符串中调用string属性。
  • 最后,我们将font属性添加到目标属性字符串中,该属性具有从上一步计算出的给定范围。

每次我们调用attributedString.string ,实际上是在跨语言桥接字符串。 我们从Swift执行上下文开始。 但是, NSMutableAttributedString的实现在Objective-C中。 因此,为了执行此操作,我们实际上必须参考原始实现。 原始实现返回的NSString是引用类型,因此当我们将其转换为String ,需要进行桥接,逐个群集地逐个字符地绘制图形。 然后我们执行两次,一次是在创建Range变量时,另一次是在转换为NSRange 。 请记住,无论是返回类型还是参数,都会发生桥接。 这个过程可能需要数十到数百毫秒的时间,这看似微不足道,但可以想象一下这是否发生在循环内。 肯定有更好的办法。

在这里,当我们调用attributedString.string ,我们将其直接转换为NSString 。 由于这是Objective-C的实现,因此不需要桥接。 并且由于它是一个NSString ,当我们调用range(of:)方法时,它实际上返回一个NSRange ,最后将其传递给addAttributes(_:range:)方法。

当我们调用nsString.range(of:stringToDecorate)时,这里还有一座小桥。 回想一下这里的stringToDecorateString类型的Swift值。 每次我们调用Objective-C range(of:) ,实际上我们都会将该String桥接到NSString 。 同样,这是一项不昂贵的任务,但请考虑一下,如果这是在循环之内,那么随着时间的推移,这笔小额费用就会加起来。 因此,避免反复桥接小字符串。

结论

这篇文章的目的不是要给您一个在Swift和Objective-C之间架桥时提高性能的解决方案,而是要让人们对该概念有所了解。 因此,下次您必须在Swift和Objective-C之间架起桥梁时,请密切注意这种进展。 请记住以下几点:

  • 在实际进行任何优化之前,请务必先衡量桥接成本。 您不应该花费太多时间来优化一段不会对您的项目带来太多性能影响的代码。
  • 在进行时间配置时,要特别注意代码在两种语言之间的交叉位置,尤其是在循环内。
  • 将会发生一些桥接,这完全可以。 对代码进行性能分析有助于您确定桥接过程的热点。 您在概要文件中寻找的是花费在您未编写的代码上的时间不成比例的时间,其中还包含单词bridge。