golang defer 关键字解析

关于defer的解释有很多,今天我从我自己的角度来说说defer

问题 1.什么是defer?

在一个函数执行到return一步的时候,实际执行的并不仅仅是return这一个操作,他不是原子的,解析golang代码的汇编语言可以发现,在计算机底层把return分为了多步去执行。比如return 1

// 伪代码 
reval = 1
defer_func
ret

很明显在函数结束的最后的时候执行了defer

问题 2.defer有什么作用?

defer的作用在于函数执行完毕之后无论如何也会执行defer中的代码。举个例子

func function(){
 	file1,err := os.Open("anyfile.txt")
 	file2,err := os.Open("anyfile2.txt")
    .......
    defer file1.Close()  	
 	defer file2.Close()  
 	return 
  } 

这样就保证了文件一定能被关闭。无论函数遇到panic等等的异常错误,都会执行defer。

(重要)问题 3. defer的原理和几种使用方法?
关于底层的原理主要是defer之后的代码堆栈与正常的不同,详细的过程我引用别人的一段话

每次defer语句执行的时候,会把函数“压栈”,函数参数会被拷贝下来;当外层函数(非代码块,如一个for循环)退出时,defer函数按照定义的逆序执行;如果defer执行的函数为nil, 那么会在最终调用函数的产生panic.defer语句并不会马上执行,而是会进入一个栈,函数return前,会按先进后出的顺序执行。也说是说最先被定义的defer语句最后执行。先进后出的原因是后面定义的函数可能会依赖前面的资源,自然要先执行;否则,如果前面先执行,那后面函数的依赖就没有了。
在defer函数定义时,对外部变量的引用是有两种方式的,分别是作为函数参数和作为闭包引用。作为函数参数,则在defer定义时就把值传递给defer,并被cache起来;作为闭包引用的话,则会在defer函数真正调用时根据整个上下文确定当前的值。

接下来我讲讲使用方法(重要)
1.使用闭包的情况下

func main(){
	b := 1
	defer func() {
		fmt.Println(b)
	}()
	b ++
	return
}

输出:2
解释:看代码可知,defer后的匿名函数使用了闭包,什么是闭包?,你可以理解为函数使用了函数内未定义的外部变量.
在return之后,defer执行了打印变量b的代码,这时的b是使用的上层函数的b,所以b为2.

2.不使用闭包1。

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

输出:1
解释:为什么这次的输出为1?因为在函数压栈的过程中(也就是顺序执行 执行到defer那一行时候)
b就被作为参数传入了defer定义的匿名函数 只不过没执行罢了。待return的时候执行defer后的代码的时候,
直接就把之前压栈的时候传入的参数b给输出了,defer后面几行的代码是否改变了b 对defer并没有影响。

3.不使用闭包2

func main(){
	b := []int{1,2,3}
	defer func(num []int) {
		fmt.Println(num)
	}(b)
	b[0] = 2
	return
}

输出:{2,2,3}
解释:为什么这次的输出为2? 很简单,即使在defer后的匿名函数中,也有‘值传递和引用传递’的区别,
切片b是一个引用类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值