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 +
操作并要求编译器推断所有的参数时,你所要求的比第一眼看到的要多得多。
推理可能会变得复杂 – 例如,如果使用+
添加UInt8
和Int
,则输出将是Int
,但有一些工作可用于评估混合types与运算符的规则。
当你使用文字时,比如你的例子中的String
文字,编译器负责将String
文字转换为String
,然后进行推理和返回+
运算符types的工作。
如果一个expression式足够复杂 – 也就是说,它需要编译器对参数和操作符做太多的推断 – 它会退出并告诉你它已经退出。
一旦expression式达到一定程度的复杂性,编译器就会退出。 另一种方法是让编译器试着去做,看看它是否可以,但是这是有风险的 – 编译器可能会继续尝试,停顿或者只是崩溃。 所以我的理解是,编译器不会超越expression式的复杂性是一个静态的阈值。
我的理解是,Swift团队正在编译优化,这将使这些错误不太常见。 点击这个链接,你可以在苹果开发者论坛上了解一点 。
在开发论坛上,Chris Lattner已经要求人们将这些错误归档为雷达报告,因为他们正在积极研究这些错误。
这是我在这里和Dev论坛上阅读了大量的文章后对它的理解,但是我对编译器的理解是天真的,我希望有更深入的知识来处理这些任务的人将扩展我写在这里。
这与接受的答案几乎相同,但增加了一些对话(我与Rob Napier和另一位Cocoahead聚会的朋友)以及链接。
请参阅此讨论中的评论。 其要点是:
+
运算符被大量超载,到目前为止,它有27个不同的函数,所以如果你连接4个string,也就是说你有3 +
运算符,编译器每次需要27个运算符,所以这是27 ^ 3次。 但是,那不是。
还有一个检查 ,看看+
函数的lhs
和rhs
是否都是有效的,如果它是通过核心调用的append
调用。 在那里你可以看到有一些有些密集的检查可能发生。 如果string是不连续存储的,如果你正在处理的string实际上是桥接到NSString的话,情况就是这样。 然后Swift必须重新组装所有的字节数组缓冲区到一个连续的缓冲区中,并且需要在这个过程中创build新的缓冲区。 然后你最终得到一个包含你试图连接在一起的string的缓冲区。
简而言之,有3个编译器检查集群会减慢你的速度,因此使用插值连接string,即使用" My fullName is \(firstName) \(LastName)"
比"My firstName is" + firstName + LastName
要好得多插值没有任何重载
Swift 3做了一些改进。 有关更多信息,请阅读如何合并多个数组而不减慢编译器的速度?