跳转至

defer

整个方法执行之后最后执行,当前作用域结束后随之执行。

defer {} 里的代码会在当前代码块返回的时候执行,无论当前代码块是从哪个分支return 的,即使程序抛出错误,也会执行。

如果多个 defer 语句出现在同一作用域中,则它们出现的顺序与它们执行的顺序相反,也就是先出现的后执行。

这里我们看一个简单的例子:

func f() {
    defer { print("First defer") }
    defer { print("Second defer") }
    print("End of function")
}
f()

结果输出 End of function Second defer First defer

使用场景有哪些

1、closeFile

如果当前方法中多次出现 closeFile ,那么我们就可以使用 defer 

func append(string: String, terminator: String = "\n", toFileAt url: URL) throws {
    // The data to be added to the file
    let data = (string + terminator).data(using: .utf8)!

     defer{
        fileHandle.closeFile()
     }

    // If file doesn't exist, create it
    guard FileManager.default.fileExists(atPath: url.path) else {
        try data.write(to: url)
        return
    }

    // If file already exists, append to it
    let fileHandle = try FileHandle(forUpdating: url)
    fileHandle.seekToEndOfFile()
    fileHandle.write(data)

}

let url = URL(fileURLWithPath: NSHomeDirectory() + "/Desktop/swift.txt")
try append(string: "Swift", toFileAt: url)

这样我们就不仅能够统一处理当前关闭文件的功能,还能防止因为忘记 closeFile 而造成的资源浪费。

2、在使用指针的时候

let count = 2
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
pointer.initialize(repeating: 0, count: count)
defer {//方法运行结束后调用,管理析构 释放
  pointer.deinitialize(count: count)
  pointer.deallocate()
}

3、请求网络的时候

在进行网络请求的时候,可能有不同的分支进行回调函数的执行

func netRquest(completion: () -> Void) {
    defer {
        self.isLoading = false
        completion()
    }
    guard error == nil else { return }
}

defer使用注意事项

defer要在guard前面

比如下面这段代码

func test() {
  guard false else { return }
  defer {
    print("defer excute")
  }
}
//test()//调用test,如果guard返回了,就不会执行defer,所以要避免这样写

同样的,有的 Swift 初学者在刚开始写 Swift 的时候可能会写这样的代码

func test() throws {
  if true {
    throw NSError()
  }

  //同样不会执行
  defer {
    print("defer excute")
  }
}

一道关于defer的面试题 

var a = 1
func add() -> Int {
    defer {
        a = a + 1
    }
    return a
}

var tmp = add()
print(tmp)//1
print(a)    //2