我觉得这比任何事都更有趣。我修好了,但我想知道原因。这是错误: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);
}
修复是:
var statement = "create table if not exists " + self.tableName();
statement += "(" + columns + ");";
这也有效(通过@efischency),但我不喜欢它,因为我觉得(
迷路了:
var statement = "create table if not exists \(self.tableName()) (\(columns))"
答案 0 :(得分:177)
我不是编译器方面的专家 - 我不知道这个答案是否会“以一种有意义的方式改变你的想法”,但我对这个问题的理解是这样的:
它与类型推断有关。每次使用+
运算符时,Swift都必须搜索+
的所有可能重载,并推断出您正在使用的+
版本。我计算了+
运算符的不到30次重载。这有很多可能性,当你将4个或5个+
操作链接在一起并要求编译器推断出所有参数时,你要求的内容比初看起来要多得多。
这种推断可能会变得复杂 - 例如,如果使用UInt8
添加Int
和+
,则输出将为Int
,但有一些工作用于评估与运营商混合类型的规则。
当您使用文字时,例如示例中的String
文字,编译器正在执行将String
文字转换为String
的工作,然后执行推断+
运算符的参数和返回类型等。
如果表达式足够复杂 - 也就是说,它需要编译器对参数和运算符做出太多推论 - 它会退出并告诉你它已退出。
一旦表达式达到一定程度的复杂性,编译器就退出是故意的。另一种方法是让编译器尝试并执行它,并查看它是否可以,但这有风险 - 编译器可能会继续尝试,陷入困境,或者只是崩溃。所以我的理解是,表达式的复杂性有一个静态阈值,编译器不会超越它。
我的理解是,Swift团队正致力于编译器优化,这些优化会使这些错误不那么常见。 You can learn a little bit about it on the Apple Developer forums by clicking on this link
在Dev论坛上,Chris Lattner要求人们将这些错误归档为雷达报告,因为他们正在积极修复它们。
这是我在阅读这里和Dev论坛上的一些帖子之后理解它的方式,但我对编译器的理解是天真的,我希望有更深入了解他们如何处理这些任务的人会扩展我在这里写的是什么。
答案 1 :(得分:30)
这与接受的答案几乎相同,但有一些额外的对话(我与Rob Napier,他的其他答案以及Cocoahead聚会的另一位朋友)和链接。
请参阅this讨论中的评论。它的要点是:
+
operator严重超载,截至目前它有27种不同的功能,所以如果你连接4个字符串,即你有3个+
运算符,编译器必须检查每次27个运营商之间,这是27 ^ 3次。但那不是它。
还有check来查看lhs
函数的rhs
和+
是否有效,如果它们调用核心{{1}被叫。在那里你可以看到可能会出现一些密集的checks。如果字符串是非连续存储的,那么如果你正在处理的字符串实际上是桥接到NSString的话就是这种情况。然后,Swift必须将所有字节数组缓冲区重新组装成一个连续的缓冲区,并且需要在此过程中创建新的缓冲区。然后你最终得到一个包含你试图连接在一起的字符串的缓冲区。
简而言之,有三组编译器检查会降低你的速度,即每个子表达式都必须根据可能返回的所有内容重新考虑。因此,使用append
将字符串与插值连接起来要比" My fullName is \(firstName) \(LastName)"
好得多,因为插值不会有任何重载
Swift 3 已经进行了一些改进。有关详细信息,请参阅How to merge multiple Arrays without slowing the compiler down?
Rob Napier在SO上的其他类似答案:
Why string addition takes so long to build?
How to merge multiple Arrays without slowing the compiler down?
答案 2 :(得分:16)
答案 3 :(得分:2)
我有类似的问题:
expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions
在Xcode 9.3中,行如下:
let media = entities.filter { (entity) -> Bool in
将其更改为以下内容后:
let media = entities.filter { (entity: Entity) -> Bool in
一切顺利。
可能它与Swift编译器有关,试图从代码中推断出数据类型。
答案 4 :(得分:0)
好消息 - 这似乎在即将发布的 Xcode 13 中得到修复。
我正在为此提交雷达报告:
http://openradar.appspot.com/radar?id=4962454186491904 https://bugreport.apple.com/web/?problemID=39206436
... Apple 刚刚确认此问题已修复。
我已经使用复杂表达式和 SwiftUI 代码测试了所有案例,并且在 Xcode 13 中一切似乎都很好。
<块引用>嗨,亚历克斯, 感谢您的耐心等待,也感谢您的反馈。我们相信这个问题已经解决。 请使用最新的 Xcode 13 beta 2 版本进行测试,并通过登录 https://feedbackassistant.apple.com 或使用反馈助手应用更新您的反馈报告和结果。