Search This Blog

Monday, June 25, 2018

Swift Defer Statement

defer keyword is used for declaring a block of code that will wait and will be executed just before exit from current scope (function, closure, loop, etc. ).  The block of code inside defer statement is guaranteed to be executed regardless of how we exit from current scope: normally, from a guard, or because of error.

Simple example
func testDefer1() {
    defer {
        print("Defer step 3")
    }
    print("Normal step 1")
    print("Normal step 2")
}

testDefer1()

//Normal step 1
//Normal step 2
//Defer step 3

defer scope


As mentioned before, defer statement will be executed before exit from current scope. So, if scope that contains defer statement is inside other scope, than defer will be executed on exit from inner scope.

defer scope example
func testDefer2() {
    print("Normal step 1")
    customFunc()
    print("Normal step 2")
}

func customFunc() {
    print("Custom step 1")
    print("Custom step 2")
    defer {
        print("Custom defer step 3")
    }
}

testDefer2()

//Normal step 1
//Custom step 1
//Custom step 2
//Custom defer step 3
//Normal step 2

As we can see here:
  1. Firstly will be printed strings from begin of outer scope
  2. Secondly will be printed strings from inner scope
  3. Thirdly will be printed strings from defer that in inner scope (just before exit from inner scope)
  4. Fourthly will be printed strings from end of outer scope

defer inside for loop


If we will use defer inside, for example, 'for loop', defer statement will be executed before end of each iteration of the loop.

Example of usage defer inside for loop
func testDefer3() {
    for i in 1...5 {
        print("Normal step iteration begins \(i)")
        defer {
            print("Defer step \(i)")
        }
        print("Normal step iteration ends \(i)")
    }
}

testDefer3()

//Normal step iteration begins 1
//Normal step iteration ends 1
//Defer step 1
//Normal step iteration begins 2
//Normal step iteration ends 2
//Defer step 2
//Normal step iteration begins 3
//Normal step iteration ends 3
//Defer step 3
//Normal step iteration begins 4
//Normal step iteration ends 4
//Defer step 4
//Normal step iteration begins 5
//Normal step iteration ends 5
//Defer step 5

Multiple defer statements


If there are multiple defer statements inside scope they will be executed in reverse order. Their execution will follow LIFO pattern - Last In First Out.

Multiple defer example
func testMultipleDefer() {
    print("Normal step 1")
    defer {
        print("Defer step 1")
    }
    defer {
        print("Defer step 2")
    }
    defer {
        print("Defer step 3")
    }
    print("Normal step 2")
}

testMultipleDefer()

//Normal step 1
//Normal step 2
//Defer step 3
//Defer step 2
//Defer step 1

As you can see, order of execution is reversed to order of adding to scope.


defer limitations


There are some limitations for using defer statement. You cannot exit from body of defer statement with following things:
  1. call return
  2. call break
  3. throw an error


Where we can use defer in Real Life Applications


The defer statement can be very useful when we want to clean up before exit from scope (for example, close connections to database or file, release unneeded resources), even if an error is thrown. For example, when we work with file and open a file to write to, we will want to make sure that we close opened file, even if there is an error happened while we work with file. 

Example of usage defer statement when working with file
func writeToFile(filename: String, text: String) throws {
    if let directory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
        
        let path = directory.appendingPathComponent(filename).absoluteString
        
        guard let file = FileHandle(forUpdatingAtPath: path) else {
            print("File open failed")
            throw FileError.fileNotFound(reason: "File not found")
        }
        
        defer {
            print("Close file...")
            file.closeFile()
        }
        
        let fileURL = directory.appendingPathComponent(filename)
        
        do {
            try text.write(to: fileURL, atomically: false, encoding: .utf8)
        }
        catch {
            throw FileError.cannotWriteToFile(reason: "Cannot write to file")
        }
    }
}
In code snippet above, we close file before exit from function. And we close file in defer statement to make sure that we close file at any case, even if error was being thrown.


Source Code


Source code for this article could be found here on GitHub

7 comments:

  1. Thanks for sharing such useful information. Thanks You I Read Your article its really informative. Keep up the good work. I hope to see more interesting articles from you.
    -Custom Website Design Company

    ReplyDelete
  2. There are many articles circulating on internet that exaggerate about Custom Designed Websites. But your article is an exception that made me understand it without any difficulty.

    ReplyDelete
  3. Thank you very much for bringing this to my attention. Your blog is chock-full of useful information. I'd have no idea unless I read that. I'll return for more excellent content. Best wishes and best of success to you. Best Custom Websites

    ReplyDelete
  4. Thanks to your article now I understand or learn many new things which are very difficult to understand the way you describe this topic is very easy to understand. Web Design USA

    ReplyDelete
  5. I adore your work, and it greatly motivates me.
    <a href="https://www.iptvfilms.com/things-to-think-about-before-hiring-a-sem-agency/> SEM Agency</a>

    ReplyDelete
  6. This is one of the nicest blogs I've ever seen, and it's really

    pleasant. This is a very valuable blog for me, and one of the most

    helpful blogs I've ever seen.
    Search Engine Marketing Services

    ReplyDelete