Swift编译器错误:字符串连接上的"表达式太复杂"

Ken*_*lor 139 xcode functional-programming compiler-errors ios swift

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

编译器指向该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);
}
Run Code Online (Sandbox Code Playgroud)

修复是:

var statement = "create table if not exists " + self.tableName();
statement += "(" + columns + ");";
Run Code Online (Sandbox Code Playgroud)

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

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

Aar*_*sen 180

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

它与类型推断有关.每次使用+运算符时,Swift都必须搜索所有可能的重载+并推断出+您使用的是哪个版本.我为+操作员计算了不到30次的重载.这是很多可能性,当你将4或5个+操作链接在一起并要求编译器推断所有参数时,你要求的内容远远超过乍一看.

这种推断可能会变得复杂 - 例如,如果你添加一个UInt8和一个Int使用+,输出将是一个Int,但是有一些工作用于评估混合类型与运算符的规则.

当您使用文字时,String例如示例中的文字,编译器执行将String文字转换为a String的工作,然后执行推断操作的参数和返回类型的工作+等.

如果表达式足够复杂 - 即,它需要编译器对参数和运算符做出太多推断 - 它会退出并告诉您它退出.

一旦表达式达到一定程度的复杂性,编译器就退出是有意的.另一种方法是让编译器尝试并执行它,并查看它是否可以,但这有风险 - 编译器可能会继续尝试,陷入困境,或者只是崩溃.所以我的理解是,表达式的复杂性有一个静态阈值,编译器不会超越它.

我的理解是,Swift团队正致力于编译器优化,这些优化会使这些错误不那么常见.您可以通过单击此链接在Apple Developer论坛上了解一些相关信息.

在Dev论坛上,Chris Lattner要求人们将这些错误归档为雷达报告,因为他们正积极致力于修复它们.

这就是我在这里以及关于它的Dev论坛上阅读了一些帖子之后的理解,但我对编译器的理解是天真的,我希望有更深入了解他们如何处理这些任务的人会扩展我的内容写在这里.

  • 这是一个错误,无论他们是否会称之为.其他语言的编译器与代码类似的代码没有问题.建议最终用户应该修复它是愚蠢的. (27认同)
  • @John如果你问我,不是一个错误,只是糟糕的语言设计!斯威夫特只是试图与众不同. (9认同)
  • 类型推断?在这种荒谬的情况下,拥有像Swift这样的强类型语言(在这种情况下你甚至无法连接String + Int而不必转换Int)有什么意义呢?Swift再次尝试解决一开始没有人遇到的问题. (6认同)

Hon*_*ney 31

这与接受的答案几乎相同,但有一些额外的对话(我与Rob Napier,他的其他答案以及Cocoahead聚会的另一位朋友)和链接.

请参阅讨论中的评论.它的要点是:

+运营商的负荷过重,截至目前有27个不同的功能,所以如果你是串联4串,即你有3个+运营商的编译器来检查,每次27个运营商之间,所以这是27 ^ 3倍.但那不是它.

还有一个检查,看看是否lhsrhs+功能是,如果他们是它调用通过核心的两个有效的append调用.在那里你可以看到可能会发生一些密集的检查.如果字符串是非连续存储的,那么如果你正在处理的字符串实际上是桥接到NSString的话就是这种情况.然后,Swift必须将所有字节数组缓冲区重新组装成一个连续的缓冲区,并且需要在此过程中创建新的缓冲区.然后你最终得到一个包含你试图连接在一起的字符串的缓冲区.

简而言之,有3个编译器检查集群会降低你的速度,即每个子表达式都必须根据它可能返回的所有内容重新考虑.因此,使用插值连接字符串即使用" My fullName is \(firstName) \(LastName)""My firstName is" + firstName + LastName插值没有任何重载要好得多

Swift 3 做了一些改进.有关更多信息,请参阅如何在不降低编译器速度的情况下合并多个数组?


Rob Napier在SO上的其他类似答案:

为什么字符串添加需要很长时间来构建?

如何在不降低编译器速度的情况下合并多个数组?

Swift Array包含的函数使构建时间变长


kar*_*rim 18

无论你说什么,这都很荒谬!:)

在此输入图像描述

但这很容易通过

return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"
Run Code Online (Sandbox Code Playgroud)