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.
See the comments in this discussion. The gist of it is:
+
is heavily overloaded (Apple seems to have fixed this for some cases)
The +
operator is 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 check between 27 operators each time, so that's 27^3 times. But that's not it.
There is also a 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 that 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.
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 might return. As a result concatenating strings with interpolation ie using " My fullName is \(firstName) \(LastName)"
is much better than "My firstName is" + firstName + LastName
since interpolation doesn't have any overloading
Swift 3 has made some improvements. 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
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 concatenatedOptionals
is.
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
The root cause of the problem is that the compiler doesn't identify the ""
as a String
. It's actually 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 emptyString
which is hardcoded to String
, the compiler no longer needs to loop through all conforming types of ExpressibleByStringLiteral
To learn how to log compilation times see here or here
Other similar answers by Rob Napier on 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
var statement = "create table if not exists \(self.tableName()) (\(columns))"
? – Yorgos+
. – Sloan