Swift Compiler Error: "Expression too complex" on a string concatenation
Swift Compiler Error: "Expression too complex" on a string concatenation
我觉得这比什么都有趣。我已经修好了,但我想知道原因。这是错误: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))"
我不是编译器方面的专家 - 我不知道这个答案是否会 "change how you think in a meaningful way," 但我对问题的理解是这样的:
它与类型推断有关。每次您使用 +
运算符时,Swift 都必须搜索 +
的所有可能重载并推断您正在使用哪个版本的 +
。我数了一下 +
运算符的重载次数不到 30 次。这有很多可能性,当您将 4 或 5 个 +
操作链接在一起并要求编译器推断所有参数时,您要求的比乍一看要多得多。
该推论可能会变得复杂 - 例如,如果您使用 +
添加 UInt8
和 Int
,输出将是 Int
,但是有一些评估混合类型与运算符规则的工作。
并且当您使用文字时,例如示例中的 String
文字,编译器会将 String
文字转换为 String
,然后执行为 +
运算符等推断参数和 return 类型的工作
如果表达式足够复杂 - 即,它需要编译器对参数和运算符进行太多推断 - 它会退出并告诉你它退出了。
让编译器在表达式达到一定的复杂程度时退出是有意为之的。另一种方法是让编译器尝试并执行它,看看它是否可以,但这是有风险的——编译器可能会一直尝试下去,陷入困境,或者只是崩溃。所以我的理解是,编译器不会超过表达式复杂度的静态阈值。
我的理解是 Swift 团队正在致力于编译器优化,以减少这些错误的发生。 You can learn a little bit about it on the Apple Developer forums by clicking on this link.
在开发论坛上,Chris Lattner 要求人们将这些错误作为雷达报告提交,因为他们正在积极修复它们。
这就是我在阅读此处和 Dev 论坛上关于它的大量帖子后的理解方式,但我对编译器的理解是幼稚的,我希望有人对他们如何处理这些任务有更深入的了解将扩展我在这里写的内容。
这与公认的答案几乎相同,但增加了一些对话(我与 Rob Napier、他的其他答案以及来自 Slack 的 Matt、Oliver、David)和链接。
查看 讨论中的评论。它的要点是:
+
严重超载(Apple 似乎已经修复了某些情况)
+
operator 严重超载,截至目前它有 27 个不同的函数,因此如果您连接 4 个字符串,即您有 3 个 +
运算符,编译器必须 检查 每次有 27 个运算符,所以是 27^3 次。但事实并非如此。
还有一个可能发生的check to see if the lhs
and rhs
of the +
functions are both valid if they are it calls through to core the append
called. There you can see there are a number of somewhat intensive checks。如果字符串是非连续存储的,如果你正在处理的字符串实际上是桥接到 NSString 的,这似乎就是这种情况。 Swift 然后必须将所有字节数组缓冲区重新 assemble 到一个连续的缓冲区中,这需要在此过程中创建新的缓冲区。然后你最终得到一个缓冲区,其中包含你试图连接在一起的字符串。
简而言之,有 3 组编译器检查会减慢您的速度,即 每个子表达式都必须根据它 可能 的所有内容重新考虑return。因此,使用插值连接字符串即使用 " My fullName is \(firstName) \(LastName)"
比 "My firstName is" + firstName + LastName
好得多,因为插值 没有 有任何重载
Swift 3 进行了一些 改进。有关详细信息,请阅读 。尽管如此,+
运算符仍然过载,最好对较长的字符串使用字符串插值
选项的使用(持续的问题 - 可用的解决方案)
在这个非常简单的项目中:
import UIKit
class ViewController: UIViewController {
let p = Person()
let p2 = Person2()
func concatenatedOptionals() -> String {
return (p2.firstName ?? "") + "" + (p2.lastName ?? "") + (p2.status ?? "")
}
func interpolationOptionals() -> String {
return "\(p2.firstName ?? "") \(p2.lastName ?? "")\(p2.status ?? "")"
}
func concatenatedNonOptionals() -> String {
return (p.firstName) + "" + (p.lastName) + (p.status)
}
func interpolatedNonOptionals() -> String {
return "\(p.firstName) \(p.lastName)\(p.status)"
}
}
struct Person {
var firstName = "Swift"
var lastName = "Honey"
var status = "Married"
}
struct Person2 {
var firstName: String? = "Swift"
var lastName: String? = "Honey"
var status: String? = "Married"
}
函数的编译时间如下:
21664.28ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:16:10 instance method concatenatedOptionals()
2.31ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:20:10 instance method interpolationOptionals()
0.96ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:24:10 instance method concatenatedNonOptionals()
0.82ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:28:10 instance method interpolatedNonOptionals()
请注意 concatenatedOptionals
的编译持续时间有多长。
这可以通过以下方式解决:
let emptyString: String = ""
func concatenatedOptionals() -> String {
return (p2.firstName ?? emptyString) + emptyString + (p2.lastName ?? emptyString) + (p2.status ?? emptyString)
}
在88ms
中编译
问题的根本原因是编译器没有将 ""
识别为 String
。实际上是 ExpressibleByStringLiteral
编译器将看到 ??
并且必须 遍历所有符合此协议的类型 ,直到它找到可以作为默认值的类型String
。
通过使用硬编码为 String
的 emptyString
,编译器不再需要遍历 ExpressibleByStringLiteral
的所有符合类型
要了解如何记录编译时间,请参阅 here or
Rob Napier 在 SO 上的其他类似回答:
Why string addition takes so long to build?
我有类似的问题:
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 编译器试图从周围的代码推断数据类型有关。
这话怎么说都太可笑了! :)
但这很容易通过
return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"
好消息 - 这似乎在即将到来的 Xcode 13.
中得到解决
我正在为此提交雷达报告:
http://openradar.appspot.com/radar?id=4962454186491904
https://bugreport.apple.com/web/?problemID=39206436
...Apple 刚刚确认此问题已修复。
我已经用复杂的表达式和 SwiftUI 代码测试了所有案例,在 Xcode 13 中似乎一切正常。
Hi Alex,
Thanks for your patience, and thanks for your feedback. We believe this issue is resolved.
Please test with the latest Xcode 13 beta 2 release and update your feedback report with your results by logging into https://feedbackassistant.apple.com or by using the Feedback Assistant app.
我觉得这比什么都有趣。我已经修好了,但我想知道原因。这是错误: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))"
我不是编译器方面的专家 - 我不知道这个答案是否会 "change how you think in a meaningful way," 但我对问题的理解是这样的:
它与类型推断有关。每次您使用 +
运算符时,Swift 都必须搜索 +
的所有可能重载并推断您正在使用哪个版本的 +
。我数了一下 +
运算符的重载次数不到 30 次。这有很多可能性,当您将 4 或 5 个 +
操作链接在一起并要求编译器推断所有参数时,您要求的比乍一看要多得多。
该推论可能会变得复杂 - 例如,如果您使用 +
添加 UInt8
和 Int
,输出将是 Int
,但是有一些评估混合类型与运算符规则的工作。
并且当您使用文字时,例如示例中的 String
文字,编译器会将 String
文字转换为 String
,然后执行为 +
运算符等推断参数和 return 类型的工作
如果表达式足够复杂 - 即,它需要编译器对参数和运算符进行太多推断 - 它会退出并告诉你它退出了。
让编译器在表达式达到一定的复杂程度时退出是有意为之的。另一种方法是让编译器尝试并执行它,看看它是否可以,但这是有风险的——编译器可能会一直尝试下去,陷入困境,或者只是崩溃。所以我的理解是,编译器不会超过表达式复杂度的静态阈值。
我的理解是 Swift 团队正在致力于编译器优化,以减少这些错误的发生。 You can learn a little bit about it on the Apple Developer forums by clicking on this link.
在开发论坛上,Chris Lattner 要求人们将这些错误作为雷达报告提交,因为他们正在积极修复它们。
这就是我在阅读此处和 Dev 论坛上关于它的大量帖子后的理解方式,但我对编译器的理解是幼稚的,我希望有人对他们如何处理这些任务有更深入的了解将扩展我在这里写的内容。
这与公认的答案几乎相同,但增加了一些对话(我与 Rob Napier、他的其他答案以及来自 Slack 的 Matt、Oliver、David)和链接。
查看
+
严重超载(Apple 似乎已经修复了某些情况)
+
operator 严重超载,截至目前它有 27 个不同的函数,因此如果您连接 4 个字符串,即您有 3 个 +
运算符,编译器必须 检查 每次有 27 个运算符,所以是 27^3 次。但事实并非如此。
还有一个可能发生的check to see if the lhs
and rhs
of the +
functions are both valid if they are it calls through to core the append
called. There you can see there are a number of somewhat intensive checks。如果字符串是非连续存储的,如果你正在处理的字符串实际上是桥接到 NSString 的,这似乎就是这种情况。 Swift 然后必须将所有字节数组缓冲区重新 assemble 到一个连续的缓冲区中,这需要在此过程中创建新的缓冲区。然后你最终得到一个缓冲区,其中包含你试图连接在一起的字符串。
简而言之,有 3 组编译器检查会减慢您的速度,即 每个子表达式都必须根据它 可能 的所有内容重新考虑return。因此,使用插值连接字符串即使用 " My fullName is \(firstName) \(LastName)"
比 "My firstName is" + firstName + LastName
好得多,因为插值 没有 有任何重载
Swift 3 进行了一些 改进。有关详细信息,请阅读 +
运算符仍然过载,最好对较长的字符串使用字符串插值
选项的使用(持续的问题 - 可用的解决方案)
在这个非常简单的项目中:
import UIKit
class ViewController: UIViewController {
let p = Person()
let p2 = Person2()
func concatenatedOptionals() -> String {
return (p2.firstName ?? "") + "" + (p2.lastName ?? "") + (p2.status ?? "")
}
func interpolationOptionals() -> String {
return "\(p2.firstName ?? "") \(p2.lastName ?? "")\(p2.status ?? "")"
}
func concatenatedNonOptionals() -> String {
return (p.firstName) + "" + (p.lastName) + (p.status)
}
func interpolatedNonOptionals() -> String {
return "\(p.firstName) \(p.lastName)\(p.status)"
}
}
struct Person {
var firstName = "Swift"
var lastName = "Honey"
var status = "Married"
}
struct Person2 {
var firstName: String? = "Swift"
var lastName: String? = "Honey"
var status: String? = "Married"
}
函数的编译时间如下:
21664.28ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:16:10 instance method concatenatedOptionals()
2.31ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:20:10 instance method interpolationOptionals()
0.96ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:24:10 instance method concatenatedNonOptionals()
0.82ms /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:28:10 instance method interpolatedNonOptionals()
请注意 concatenatedOptionals
的编译持续时间有多长。
这可以通过以下方式解决:
let emptyString: String = ""
func concatenatedOptionals() -> String {
return (p2.firstName ?? emptyString) + emptyString + (p2.lastName ?? emptyString) + (p2.status ?? emptyString)
}
在88ms
问题的根本原因是编译器没有将 ""
识别为 String
。实际上是 ExpressibleByStringLiteral
编译器将看到 ??
并且必须 遍历所有符合此协议的类型 ,直到它找到可以作为默认值的类型String
。
通过使用硬编码为 String
的 emptyString
,编译器不再需要遍历 ExpressibleByStringLiteral
要了解如何记录编译时间,请参阅 here or
Rob Napier 在 SO 上的其他类似回答:
Why string addition takes so long to build?
我有类似的问题:
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 编译器试图从周围的代码推断数据类型有关。
这话怎么说都太可笑了! :)
但这很容易通过
return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"
好消息 - 这似乎在即将到来的 Xcode 13.
中得到解决我正在为此提交雷达报告:
http://openradar.appspot.com/radar?id=4962454186491904 https://bugreport.apple.com/web/?problemID=39206436
...Apple 刚刚确认此问题已修复。
我已经用复杂的表达式和 SwiftUI 代码测试了所有案例,在 Xcode 13 中似乎一切正常。
Hi Alex, Thanks for your patience, and thanks for your feedback. We believe this issue is resolved. Please test with the latest Xcode 13 beta 2 release and update your feedback report with your results by logging into https://feedbackassistant.apple.com or by using the Feedback Assistant app.