defer 用法

目录

1、资源释放

2、异常捕获

3、参数的预计算

4、defer 返回值的陷阱


1、资源释放

下面是一个简单的读取文件的程序,os.Open 打开文件资源描述符,在读取文件后,需要释放资源。但是在错误的时候,程序就直接返回那么,资源就得不到释放

func ReadFile(Filename string) ([]byte, error) {
   file, err := os.Open(Filename)
   if err != nil {
      return nil, err
   }
   stat, err := file.Stat()
   if err != nil {
      return nil, err
   }
   var re = make([]byte, stat.Size())
   _, err = file.Read(re)
  if err!=nil{
    return nil,err
  }
   file.Close()
   if err != nil {
      return nil, err
   }
   return re, nil
​
}

那么程序需要就需要修改为,在所有err 的地方都需要释放资源

file, err := os.Open(Filename)
if err != nil {
   file.Close()
   return nil, err
}
  stat, err := file.Stat()
  if err != nil {
    file.Close()
    return nil, err
  }
  var re = make([]byte, stat.Size())
  _, err = file.Read(re)
  if err != nil {
    file.Close()
    return nil, err
  }

但是这样处理很不优雅,而且很容易漏掉,那么我们就可以利用defer 的延迟调用,程序结束的时候释放资源,能减少大量冗余代码避免由于忘记释放资源而产生的错误

func ReadFile(Filename string) ([]byte, error) {
   file, err := os.Open(Filename)
   defer file.Close()
   if err != nil {
      return nil, err
   }
   stat, err := file.Stat()
   if err != nil {
      return nil, err
   }
   var re = make([]byte, stat.Size())
   _, err = file.Read(re)
   if err != nil {
      return nil, err
   }
   if err != nil {
      return nil, err
   }
   return re, nil
​
}

2、异常捕获

避免程序因为panic 异常退出,可以通过defer函数中使用recover进行异常捕获,程序就不会异常退出,main的 fmt.Println 可以打印

func ReadFile(Filename string) ([]byte, error) {
   file, err := os.Open(Filename)
   defer file.Close()
   if err != nil {
      return nil, err
   }
   stat, err := file.Stat()
   if err != nil {
      return nil, err
   }
   var re = make([]byte, stat.Size())
   _, err = file.Read(re)
   if err != nil {
      return nil, err
   }
   if err != nil {
      return nil, err
   }
   return re, nil
​
}

3、参数的预计算

传递到defer 中的函数参数是预执行的,因此在执行 defer 语句时,执行了a+1并将其保留下来,只到函数执行完后才执行 defer 函数体内的语句。

func main() {
   a := 1
   defer func(b int) {
      fmt.Println(b)
   }(a + 1)
   a = 100
}

4、defer 返回值的陷阱

有返回值时时先执行defer 还是先执行 return ,具体情况,具体分析,先看几个例子:

var g = 100
func gf() (r int) {
   defer func() {
      g = 200
   }()
   fmt.Printf("g:%d\n", g)
   return g
}
​
func main() {
   i := gf()
   fmt.Println(i)
   fmt.Println(g)
}
g:100
100
200

从返回结果看好像是先执行了return 在执行了 defer

那么下面的程序:

var g = 100
​
func gf() (r int) {
  r = g
  defer func() {
    r = 200
  }()
  r = 0
  return r
}
​
func main() {
  i := gf()
  fmt.Println(i)
  fmt.Println(g)
}

返回结构:

200
100

从返回结果好像是先执行了defer 后执行了 return

那么为什么会这样呢,原因是return 不是一个原子操作,包含了下面几步:

将返回值保存在栈上->执行defer 函数->函数返回

对于第一个例子:

g=100
r=g
g=200
return

对于第二个例子

g=100
r=g
g=200
return

你可能感兴趣的:(ios,前端,javascript,golang,后端,defer,用法)