I was asked to migrate a rather large app to Swift 2. The compiler keeps throwing segmentation fault: 11 errors for one function, present in different modules of the app's logic (only difference being variables used):
func loadMoreContent() {
if let collection = self.ratingsCollection where collection.identifier != 0,
let totalEntries = collection.totalEntries,
let objects = self.ratings?.count where objects < totalEntries {
self.ratingsCollection = nil
collection.nextPage().onSuccess { (value) in
if let collection = value as? Collection<Rating> {
self.ratingsCollection = collection
} else {
self.ratingsCollection = Collection<Rating>(identifier: 0)
}
}.onFailure { error in
self.ratingsCollection = Collection<Rating>(identifier: 0)
}
}
}
Here are the errors themselves:
1. While type-checking 'loadMoreContent' at (path redacted).swift:46:3
2. While type-checking expression at [(path redacted).swift:54:9 - line:64:9]
RangeText="collection.nextPage().onSuccess { (value) in
if let collection = value as? Collection<Rating> {
self.ratingsCollection = collection
} else {
self.ratingsCollection = Collection<Rating>(identifier: 0)
}
}.onFailure { error in
self.ratingsCollection = Collection<Rating>(identifier: 0)
}"
3. While loading members for declaration 0x7fdda42ea2b0 at <invalid loc>
4. While deserializing 'producer' (FuncDecl #340)
Does anyone have any idea what can be wrong with this function at first glance? I should add it compiles with no changes in Xcode 6 / Swift 1.2.
This is a hair pulling error especially common in XCode7.
Occasionally the usual XCode stupid bug protocol (clean, XCode Restart, clean, build) fixes it. However, often it is due to one or more offending lines of code. This doesn't necessarily mean there is a bug in the code, either!
So, before restarting, it is sometimes useful to undo recent changes sequentially and trying to build as you go along. If any of your dependencies or frameworks have been updated since your last successful build, these could be a likely candidate.
There are a couple things that seem to produce this error fairly regularly. So please add to this list concisely if you can isolate specific issues that CONSISTENTLY cause errors for you:
1) String concatenation using the plus operator in calls to methods that use autoclosures (found in calls to XCGLogger):
public func myFunc(#autoclosure closure: () -> String?){
// do something
}
someInstance.myFunc("Hi " + nameStr + "!")
2) failure to call super.init() from subclass especially when super class is using a default initializer (you haven't explicitly created your own init)
3) Accidentally using a single equals sign to test for equality (using = instead of == ) especially in complex statement such as in this answer.
Related
EDIT
The source of the issue seems to be the use of .lowercased() to return a lower case string. Adding this to any line seems to result in a 40-60ms compile time.
In an effort to speed up my compile times I've added the "-Xfrontend -debug-time-function-bodies" flag detailed here as well as following the optimisation tips here
The compiler seems to struggle with filter functions for example this:
// Filter by search term
if self.searchController.isActive {
filteredManualTasksArray = self.filteredManualTasksArray.filter() {
let taskName:String = $0.bmTaskName!.lowercased()
let searchText:String = searchController.searchBar.text!.lowercased()
return taskName.contains(searchText)
}
}
Result in this warning:
Expression took 51ms to type-check (limit: 50ms)
The variable filteredManualTasksArray is declared elsewhere as:
var filteredManualTasksArray:[BMTask]!
So all variables are explicitly typed as far as I can tell. Is there anything I can do to speed this up?
Edit: I've tried a couple of approaches that seem to make no difference
1) combining the three lines into one:
return $0.bmTaskName!.lowercased().contains(searchController.searchBar.text!.lowercased())
2) Specifying the filter type:
filteredManualTasksArray = self.filteredManualTasksArray.filter { (task: BMTask) -> Bool in
Edit 2
This line:
let searchText:String = searchController.searchBar.text!.lowercased()
Seems to be the cause of the issue - it take 38ms to type check. Any ideas how I can improve this?
You could move the let searchText line out of the filter function:
if self.searchController.isActive {
let searchText:String = searchController.searchBar.text!.lowercased()
filteredManualTasksArray = self.filteredManualTasksArray.filter() {
let taskName:String = $0.bmTaskName!.lowercased()
return taskName.contains(searchText)
}
}
I have encountered numerous situations where a coder have used the guard keyword. And then later, in a seemingly almost identical situation the same coder in the same code does not use the guard keyword. I am aware that this may be a stupid question, so please don't bash it. When should I use the guard keyword and where shouldn't I?
Here is an example (there are many more). This is part of a script that is requesting data form an API.
//Here I am using guard
guard let json = json else {
//Now I am not using guard
if let error = error {
completion(.Failure(error))
} else {
//Error handling
}
return
}
Why not use the:
if let var1 = var1 {
//Keep on going
} else {
//Don't crash
}
syntax all the time instead of the guard syntax? At first glance it even seems to have more functionality, but I am certain that does not have to be the case.
One great benefit of the guard statement is that you know that if the condition is not satisfied then the execution flow gets stopped.
This is important for several reasons
Unwrapping
You can define unwrapped values which don't need a new scope { ... } to be available
func next(num:Int?) -> Int? {
guard let num = num else { return nil }
return num + 1
}
Readability
When you read the code you know that if the guard condition is not satisfied then the following lines won't be executed.
Semantics
You know a guard statement is there to check conditions required for the following block of code.
But I can replace every guard with an if
Sure. We could also replace every while and for with a goto in some languages. And we could always replace recursion with iteration (and viceversa).
But this doesn't necessarily means it is always a good idea.
Despite we can implement some behaviours with more then one programming "tool", we should still use the one that better fits that specific scenario.
Now I am learning the swift and when I use the if - else ,the Xcode shows me a warniing "will not be excuted".Though it isn't a big problem, I don't want to see this, how can I clear this warning in the project?
This is from a logic error that the compiler has picked up and is warning you about.
It gives the line number in your code that can never be reached.
a) Change the logic of your code
b) delete or comment out lines of code that can never be reached
The compiler will not give this message unnecessarily
example
if 1 == 2 {
a = 3
}
else {
a = 4
}
Obviously the condition is never met, the a=3 assignment can never happen.
let a = 3;
let b = 4;
if (a == 3) {
print("executed")
} else if (a == 5) {
print("never be executed")
} else {
print("not executed")
}
Apple's LLVM compiler is pretty verbose when it tells you about warnings - if you check the logs you would find something like:
warning: will never be executed [-Wunreachable-code]
So to suppres that warning, you need to set the flag -Wno-unreachable-code. This works for the ObjC compiler, but currently isn't supported by the Swift compiler
What is the best way to handle an init that might fail in Swift? For example, you create an instance of class that depends on a certain resource that might not be available.
Apparently we have 2 options:
A bailable init that returns nil (the Cocoa way)
An init that throws an error
See below
enum ThingError: ErrorType{
case crap
}
class Thing {
init(c: Int) throws{
if c < 0 {
throw ThingError.crap
}
}
}
var c = try Thing(c: 3)
do{
var d = try Thing(c: -4)
}catch{
print("oh vey!")
}
Is there a recommended way of doing this? The second option seems more "Swifty"...
Neither is inherently better or Swiftier.
Personally I find throws initializers a huge pain. I'd much rather have a failed initializer return nil, because then I can do my initialization with guard let instead of having to wrap things in do/catch and deal with the resulting scoping issues. Your code illustrates the problem; your var d is "stuck" inside a do scope. I'd rather say this:
guard let d = Thing(c:-4) else {return}
// now d is unwrapped and in scope!
...than this (what you have to say):
do {
var d = try Thing(c: -4)
} catch {
print("oh vey!")
}
// and here there is no `d`, so _now_ what?
On the other hand, throwing an error offers an opportunity to send a message, i.e. to be communicative about exactly what went wrong. You can't do that with a mere init? initializer; it works or it fails, and that's all the caller knows.
With the code below, the local songs variable is never able to be iterated despite all the checks to the contrary ( println shows the value stored ). The other thing is that the Xcode debugger seems to jump all over the place in the init method.
let gLibraryManager = LibraryManager()
class LibraryManager {
var Songs = Dictionary<String, String>()
init() {
println("struct being initialized from NSDefaults")
let userDefaults = NSUserDefaults.standardUserDefaults();
var result:AnyObject = userDefaults.objectForKey(LIKED_LIST)
println(result)
var local = result as? Dictionary<String,String>
if local != nil {
println("local not nil: \(local!)")
for (id,title) in local! {
Songs[id] = title
}
if Songs.count > 0 {
println("NSDefaults detected: \(Songs)")
} else {
println("no NSDefaults detected. Initializing empty")
}
}
}
ok. i figured out what is was.
I had set the Swift Compiler - Code Generation. Optimization level to -Fastest. This was to prevent the extremely slow creation of Dictionaries.
However, it appears this breaks the ability to iterate structures.
It also seems to resolve the weird bouncing around of breakpoints.
This was a needle in a haystack that tooks many hours. I guess the moral of the story is not to mess with compiler flags yet.