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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29707622/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 05:40:40  来源:igfitidea点击:

Swift Compiler Error: "Expression too complex" on a string concatenation

iosxcodeswiftcompiler-errorsfunctional-programming

提问by Kendrick Taylor

I find this amusing more than anything. I've fixed it, but I'm wondering about the cause. Here is the error: DataManager.swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions. Why is it complaining? It seems like one of the most simple expressions possible.

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

The compiler points to the columns + ");";section

编译器指向的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);
}

the fix is:

解决方法是:

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

this also works (via @efischency) but I don't like it as much because I think the (get lost:

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

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

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

回答by Aaron Rasmussen

I am not an expert on compilers - I don't know if this answer will "change how you think in a meaningful way," but my understanding of the problem is this:

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

It has to do with type inference. Each time you use the +operator, Swift has to search through all of the possible overloads for +and infer which version of +you are using. I counted just under 30 overloads for the +operator. That's a lot of possibilities, and when you chain 4 or 5 +operations together and ask the compiler to infer all of the arguments, you are asking a lot more than it might appear at first glance.

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

That inference can get complicated - for example, if you add a UInt8and an Intusing +, the output will be an Int, but there's some work that goes into evaluating the rules for mixing types with operators.

该推理可能会变得复杂 - 例如,如果添加 aUInt8Intusing +,则输出将是 an Int,但有一些工作需要评估将类型与运算符混合使用的规则。

And when you are using literals, like the Stringliterals in your example, the compiler doing the work of converting the Stringliteral to a String, and then doing the work of infering the argument and return types for the +operator, etc.

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

If an expression is sufficiently complex - i.e., it requires the compiler to make too many inferences about the arguments and the operators - it quits and tells you that it quit.

如果一个表达式足够复杂——也就是说,它需要编译器对参数和运算符进行过多的推断——它会退出并告诉你它退出了。

Having the compiler quit once an expression reaches a certain level of complexity is intentional. The alternative is to let the compiler try and do it, and see if it can, but that is risky - the compiler could go on trying forever, bog down, or just crash. So my understanding is that there is a static threshold for the complexity of an expression that the compiler will not go beyond.

一旦表达式达到一定的复杂程度,让编译器退出是有意的。另一种方法是让编译器尝试去做,看看它是否可以,但这是有风险的——编译器可能会一直尝试下去,陷入困境,或者只是崩溃。所以我的理解是,对于编译器不会超出的表达式的复杂性,存在一个静态阈值。

My understanding is that the Swift team is working on compiler optimizations that will make these errors less common. You can learn a little bit about it on the Apple Developer forums by clicking on this link.

我的理解是 Swift 团队正在致力于编译器优化,以减少这些错误的发生。您可以通过单击此链接在 Apple Developer 论坛上了解有关它的一些信息

On the Dev forums, Chris Lattner has asked people to file these errors as radar reports, because they are actively working on fixing them.

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

That is how I understand it after reading a number of posts here and on the Dev forum about it, but my understanding of compilers is naive, and I am hoping that someone with a deeper knowledge of how they handle these tasks will expand on what I have written here.

这就是我在这里和 Dev 论坛上阅读了一些关于它的帖子后的理解方式,但是我对编译器的理解是天真的,我希望对他们如何处理这些任务有更深入了解的人能够扩展我的内容已经写到这里了。

回答by Honey

This is almost same as the accepted answer but with some added dialogue (I had with Rob Napier, his other answers and Matt, Oliver, David from Slack) and links.

这与接受的答案几乎相同,但增加了一些对话(我与 Rob Napier、他的其他答案以及来自 Slack 的 Matt、Oliver、David 进行了对话)和链接。

See the comments in thisdiscussion. The gist of it is:

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

+is heavily overloaded (Apple seems to have fixed this for some cases)

+严重超载(Apple 似乎在某些情况下已修复此问题)

The +operatoris heavily overloaded, as of now it has 27 different functions so if you are concatenating 4 strings ie you have 3 +operators the compiler has to checkbetween 27 operators each time, so that's 27^3 times. But that's not it.

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

There is also a checkto see if the lhsand rhsof the +functions are both valid if they are it calls through to core the appendcalled. There you can see there are a number of somewhat intensive checksthat can occur. If the string is stored non-contiguously, which appears to be the case if the string you're dealing with is actually bridged to NSString. Swift then has to re-assemble all the byte array buffers into a single contiguous buffer and which requires creating new buffers along the way. and then you eventually get one buffer that contains the string you're attempting to concatenate together.

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

In a nutshell there is 3 clusters of compiler checks that will slow you down ie each sub-expression has to be reconsidered in light of everything it mightreturn. As a result concatenating strings with interpolation ie using " My fullName is \(firstName) \(LastName)"is much better than "My firstName is" + firstName + LastNamesince interpolation doesn'thave any overloading

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

Swift 3has made someimprovements. For more information read How to merge multiple Arrays without slowing the compiler down?. Nonetheless the +operator is still overloaded and it's better to use string interpolation for longer strings

Swift 3做了一些改进。有关更多信息,请阅读如何在不降低编译器速度的情况下合并多个数组?. 尽管如此,+运算符仍然过载,最好对更长的字符串使用字符串插值



Usage of optionals (ongoing problem - solution available)

选项的使用(持续存在的问题 - 可用的解决方案)

In this very simple project:

在这个非常简单的项目中:

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"
}

The compile time for the functions are as such:

函数的编译时间如下:

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()

Notice how crazy high the compilation duration for concatenatedOptionalsis.

请注意编译持续时间有多疯狂concatenatedOptionals

This can be solved by doing:

这可以通过执行以下操作来解决:

let emptyString: String = ""
func concatenatedOptionals() -> String {
    return (p2.firstName ?? emptyString) + emptyString + (p2.lastName ?? emptyString) + (p2.status ?? emptyString)
}

which compiles in 88ms

其中编译 88ms

The root cause of the problem is that the compiler doesn't identify the ""as a String. It's actually ExpressibleByStringLiteral

问题的根本原因是编译器没有""String. 其实是ExpressibleByStringLiteral

The compiler will see ??and will have to loop through all types that have conformed to this protocol, till it finds a type that can be a default to String. By Using emptyStringwhich is hardcoded to String, the compiler no longer needs to loop through all conforming types of ExpressibleByStringLiteral

编译器将看到??并且必须遍历所有符合此协议的类型,直到找到可以作为默认值的类型String。通过使用emptyString硬编码为String,编译器不再需要遍历所有符合类型的ExpressibleByStringLiteral

To learn how to log compilation times see hereor here

要了解如何记录编译时间,请参阅此处此处



Other similar answers by Rob Napier on SO:

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

Why string addition takes so long to build?

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

How to merge multiple Arrays without slowing the compiler down?

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

Swift Array contains function makes build times long

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

回答by karim

This is quite ridiculous no matter what you say! :)

无论你说什么,这都是非常可笑的!:)

enter image description here

在此处输入图片说明

But this gets passed easily

但这很容易通过

return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"

回答by vedrano

I had similar issue:

我有类似的问题:

expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions

In Xcode 9.3 line goes like this:

在 Xcode 9.3 行中是这样的:

let media = entities.filter { (entity) -> Bool in

After changing it into something like this:

改成这样后:

let media = entities.filter { (entity: Entity) -> Bool in

everything worked out.

一切顺利。

Probably it has something to do with Swift compiler trying to infer data type from code around.

可能与 Swift 编译器试图从周围的代码推断数据类型有关。