Last active
November 7, 2015 14:53
-
-
Save JadenGeller/902fbb8ae1a86c2d5f7c to your computer and use it in GitHub Desktop.
Try/Catch Exception Handling in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A very basic implementation of try/catch blocks in Swift | |
// Note that this is not thread safe | |
// Also note that this does not support nested try/catch statements | |
// We will start with an example | |
func test(x: Bool) { | |
try{ | |
println("HI") | |
if x { throw() } | |
println("BYE") | |
} | |
catch{ e in | |
println("BAD STUFF") | |
} | |
} | |
test(true) // -> "HI" | |
// "BAD STUFF" | |
test(false) // -> "HI" | |
// "BYE" | |
// Woot, it works as expected! | |
// Let's try one more example! | |
func divide(a: Int, b: Int) -> Int { | |
if b == 0 { throw() } | |
return a / b | |
} | |
divide(6,2) // -> 3 | |
divide(6,0) // CRASH! | |
try{ divide(6,0) } // (nothing) | |
// That's right, throw() kills the program but try{ throw() } is safely handled. | |
// Now lets see how it works: | |
// Global variables. Yes, I know, gross! But I wanted to write this quickly. | |
// To make this thread-safe, make these thread specific globals. | |
// Note that this can be implemented without global variables of any kind, | |
// but we lose our beautiful, easy to use syntax. | |
// Stores the status of our try/catch statement | |
var code: Int? = nil | |
// Stores the location to jump to on a throw() call | |
var jump = UnsafeMutablePointer<Int32>(malloc(UInt(sizeof(jmp_buf)))) | |
// Boring. Note we could expand this to allow | |
// us to add further info such as a description | |
// string. This would require us to store that | |
// in some global variable just like code and jump. | |
struct Exception { | |
let code: Int | |
} | |
/* | |
* Recieves a closure to be tried. If a throw() is called | |
* within that closure, it will jump back to the Jump Position | |
* (skipping the execution of the rest of the closure) with a | |
* nonzero value for our code variable. Thus, f will not be | |
* executed a second time. | |
*/ | |
func try(f: () -> ()) { | |
code = Int(setjmp(jump)) // Jump Position | |
if code == 0 { | |
// Haven't yet tried | |
f() | |
} | |
} | |
/* | |
* Optional function to be called after a try statement | |
* that allows you to handle any errors that occured. | |
* An exception is passed in that can be used to identify | |
* what error occured in our try block. | |
*/ | |
func catch(f: Exception -> ()) { | |
if let thrownCode = code { | |
if thrownCode != 0 { | |
// Exception occured | |
f(Exception(code: thrownCode)) | |
} | |
code = nil // Prevents two successive catch blocks | |
} | |
else { | |
fatalError("A catch statement must follow some try statement") | |
} | |
} | |
// Convenience function | |
func throw() { | |
throw(-1) | |
} | |
// Jumps back to the try block skipping execution of the rest of the closure | |
func throw(errorCode: Int) { | |
if code == 0 { | |
// Fail out of try block | |
longjmp(jump, Int32(errorCode)) | |
} | |
else{ | |
// We are outside of a try block | |
fatalError("An exception was thrown with error code \(errorCode)") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment