ios - Error del compilador rápido: "Expresión demasiado compleja" en una concatenación de cadenas
xcode swift (4)
Esto me parece más divertido que nada.
Lo arreglé, pero me pregunto por la causa.
Aquí está el error:
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
.
¿Por qué se queja?
Parece una de las expresiones más simples posibles.
El compilador apunta a las
columns + ");";
sección
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);
}
la solución es:
var statement = "create table if not exists " + self.tableName();
statement += "(" + columns + ");";
esto también funciona (a través de @efischency) pero no me gusta tanto porque creo que el
(
perderse:
var statement = "create table if not exists /(self.tableName()) (/(columns))"
Esto es casi lo mismo que la respuesta aceptada, pero con un diálogo adicional (tuve con Rob Napier, sus otras respuestas y otro amigo de una reunión de Cocoahead) y enlaces.
Ver los comentarios en this discusión. La esencia de esto es:
El
operador
+
está muy sobrecargado, a partir de ahora tiene 27 funciones diferentes, por lo que si está concatenando 4 cadenas, es decir, tiene 3 operadores
+
, el compilador debe
verificar
entre 27 operadores cada vez, por lo que son 27 ^ 3 veces.
Pero eso no es todo.
También hay una
check
para ver si
lhs
y
rhs
de las funciones
+
son válidas si son llamadas al núcleo del
append
llamado.
Allí puede ver que hay una serie de
checks
algo intensivos que pueden ocurrir.
Si la cadena se almacena de forma no contigua, lo que parece ser el caso si la cadena con la que está tratando está realmente unida a NSString.
Swift luego tiene que volver a ensamblar todos los búferes de matriz de bytes en un solo búfer contiguo y eso requiere crear nuevos búferes en el camino.
y finalmente obtienes un búfer que contiene la cadena que intentas concatenar juntos.
En pocas palabras, hay 3 grupos de comprobaciones del compilador que lo retrasarán, es decir,
cada subexpresión debe reconsiderarse a la luz de todo lo que
pueda
devolver
.
Como resultado, concatenar cadenas con interpolación, es decir, usar
" My fullName is /(firstName) /(LastName)"
es mucho mejor que
"My firstName is" + firstName + LastName
ya que la interpolación
no
tiene sobrecarga
Swift 3
ha realizado
algunas
mejoras.
Para obtener más información, lea
¿Cómo fusionar varias matrices sin ralentizar el compilador?
.
No obstante, el operador
+
todavía está sobrecargado y es mejor usar la interpolación de cadenas para cadenas más largas
Otras respuestas similares de Rob Napier en SO:
¿Por qué la adición de cadenas tarda tanto en construirse?
¿Cómo fusionar varias matrices sin ralentizar el compilador?
Swift Array contiene función que hace que los tiempos de construcción sean largos
No soy un experto en compiladores. No sé si esta respuesta "cambiará su forma de pensar de manera significativa", pero mi comprensión del problema es la siguiente:
Tiene que ver con la inferencia de tipos.
Cada vez que usa el operador
+
, Swift tiene que buscar a través de todas las sobrecargas posibles para
+
e inferir qué versión de
+
está usando.
Conté poco menos de 30 sobrecargas para el operador
+
.
Esas son muchas posibilidades, y cuando encadena 4 o 5
+
operaciones juntas y le pide al compilador que infiera todos los argumentos, está pidiendo mucho más de lo que podría parecer a primera vista.
Esa inferencia puede complicarse; por ejemplo, si agrega un
UInt8
y un
Int
usando
+
, la salida será un
Int
, pero hay algo de trabajo en evaluar las reglas para mezclar tipos con operadores.
Y cuando usa literales, como los literales de
String
en su ejemplo, el compilador realiza el trabajo de convertir el literal de
String
en una
String
, y luego hace el trabajo de inferir el argumento y los tipos de retorno para el operador
+
, etc.
Si una expresión es suficientemente compleja, es decir, requiere que el compilador haga demasiadas inferencias sobre los argumentos y los operadores, se cierra y le dice que se cierra.
Hacer que el compilador se cierre una vez que una expresión alcanza un cierto nivel de complejidad es intencional. La alternativa es dejar que el compilador lo intente y ver si puede, pero eso es arriesgado: el compilador podría seguir intentándolo para siempre, atascarse o simplemente bloquearse. Entonces, entiendo que hay un umbral estático para la complejidad de una expresión que el compilador no irá más allá.
Tengo entendido que el equipo de Swift está trabajando en optimizaciones del compilador que harán que estos errores sean menos comunes. Puede aprender un poco sobre esto en los foros de desarrolladores de Apple haciendo clic en este enlace .
En los foros de desarrolladores, Chris Lattner ha pedido a las personas que presenten estos errores como informes de radar, porque están trabajando activamente para solucionarlos.
Así es como lo entiendo después de leer varias publicaciones aquí y en el foro de desarrollo al respecto, pero mi comprensión de los compiladores es ingenua, y espero que alguien con un conocimiento más profundo de cómo manejan estas tareas se expanda en lo que yo He escrito aquí.
Tuve un problema similar:
expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions
En Xcode 9.3 la línea es así:
let media = entities.filter { (entity) -> Bool in
Después de cambiarlo a algo como esto:
let media = entities.filter { (entity: Entity) -> Bool in
Todo salió bien.
Probablemente tenga algo que ver con el compilador Swift que intenta inferir el tipo de datos del código.