Swift编译器错误:string连接上的“expression式太复杂”

我觉得这很有趣。 我已经修好了,但是我想知道原因。 这里是错误: DataManager.swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions DataManager.swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions 。 为什么抱怨? 这似乎是最简单的expression式之一。

编译器指向columns + ");"; 部分

 func tableName() -> String { return("users"); } func createTableStatement(schema: [String]) -> String { var schema = schema; schema.append("id string"); schema.append("created integer"); schema.append("updated integer"); schema.append("model blob"); var columns: String = ",".join(schema); var statement = "create table if not exists " + self.tableName() + "(" + columns + ");"; return(statement); } 

解决方法是:

 var statement = "create table if not exists " + self.tableName(); statement += "(" + columns + ");"; 

这也可以(通过@efischency),但我不喜欢它,因为我觉得(迷路:

var statement = "create table if not exists \(self.tableName()) (\(columns))"

我不是编译器方面的专家 – 我不知道这个答案是否会“以一种有意义的方式改变你的想法”,但是我对这个问题的理解是这样的:

它与types推断有关。 每次使用+运算符时,Swift都必须search所有可能的重载+并推断出您使用的是哪个版本。 我为+运算符计算了超过30个重载。 这是很多的可能性,当你连接4或5 +操作并要求编译器推断所有的参数时,你所要求的比第一眼看到的要多得多。

推理可能会变得复杂 – 例如,如果使用+添加UInt8Int ,则输出将是Int ,但有一些工作可用于评估混合types与运算符的规则。

当你使用文字时,比如你的例子中的String文字,编译器负责将String文字转换为String ,然后进行推理和返回+运算符types的工作。

如果一个expression式足够复杂 – 也就是说,它需要编译器对参数和操作符做太多的推断 – 它会退出并告诉你它已经退出。

一旦expression式达到一定程度的复杂性,编译器就会退出。 另一种方法是让编译器试着去做,看看它是否可以,但是这是有风险的 – 编译器可能会继续尝试,停顿或者只是崩溃。 所以我的理解是,编译器不会超越expression式的复杂性是一个静态的阈值。

我的理解是,Swift团队正在编译优化,这将使这些错误不太常见。 点击这个链接,你可以在苹果开发者论坛上了解一点 。

在开发论坛上,Chris Lattner已经要求人们将这些错误归档为雷达报告,因为他们正在积极研究这些错误。

这是我在这里和Dev论坛上阅读了大量的文章后对它的理解,但是我对编译器的理解是天真的,我希望有更深入的知识来处理这些任务的人将扩展我写在这里。

这与接受的答案几乎相同,但增加了一些对话(我与Rob Napier和另一位Cocoahead聚会的朋友)以及链接。

请参阅此讨论中的评论。 其要点是:

+运算符被大量超载,到目前为止,它有27个不同的函数,所以如果你连接4个string,也就是说你有3 +运算符,编译器每次需要27个运算符,所以这是27 ^ 3次。 但是,那不是。

还有一个检查 ,看看+函数的lhsrhs是否都是有效的,如果它是通过核心调用的append调用。 在那里你可以看到有一些有些密集的检查可能发生。 如果string是不连续存储的,如果你正在处理的string实际上是桥接到NSString的话,情况就是这样。 然后Swift必须重新组装所有的字节数组缓冲区到一个连续的缓冲区中,并且需要在这个过程中创build新的缓冲区。 然后你最终得到一个包含你试图连接在一起的string的缓冲区。

简而言之,有3个编译器检查集群会减慢你的速度,因此使用插值连接string,即使用" My fullName is \(firstName) \(LastName)""My firstName is" + firstName + LastName要好得多插值没有任何重载

Swift 3做了一些改进。 有关更多信息,请阅读如何合并多个数组而不减慢编译器的速度?