• go词法作用域陷进


    问题

    // 创建一些目录,再将目录删除
    // 错误写法
    var rmdirs []func()
    for _, dir := range tempDirs() {
      os.MkdirAll(dir, 0755)
      rmdirs = append(rmdirs, func(){
        os.RemoveAll(dir)
    })
    }
    
    // 正确写法
    var rmdirs []func()
    for _, d := range tempDirs() {
      dir := d  // 这一步赋值比较关键,是重点
      os.MkdirAll(dir, 0755)
      rmdirs = append(rmdirs, func() {
        os.RemoveAll(dir)
    })
    }
    
    for _, rmdir := range rmdirs{
      rmdir()
    }
    

    分析

    你可能会感到困惑,为什么要在循环体中用循环变量d赋值一个新的局部变量,而不是直接使用循环变量dir
    问题的原因在于循环变量的作用域。在上面的程序中,for循环语句引入了新的词法块,
    循环变量dir在这个词法块中被声明。在该循环中生成的所有函数值都共享相同的循环变量。
    需要注意,函数值中记录的是循环变量的内存地址,而不是循环变量某一个时刻的值。以dir为例
    后续的迭代会不断更新dir的值,当删除操作执行时,for循环已完成,dir中存储的值
    等于最后一次迭代的值。这意味着,每次对os.RemoveAll的调用删除的都是相同的目录
    通常,为了解决这个问题,我们会引入一个与循环变量同名的局部变量,作为循环变量
    的副本。比如下面的变量dir,虽然看起来很怪,但很有用
    for _, dir := range tempDirs(){
      dir := dir
    }
    
    当然这个问题不仅存在基于range循环,下面的例子中,对循环变量i的使用也存在同样的问题
    var rmdirs []func()
    dirs := tempDirs()
    for i := 0;i<len(dirs);i++{
      os.MkdirAll(dirs[i], 0755)
      rmdirs = append(rmdirs, func(){
        os.RemoveAll(dirs[i])
    })
    }
    
    如果你使用go语句或者defer语句会经常遇到此类问题。这不是go或defer本身导致的,
    而是因为它们都会等待循环结束后,再执行函数值。
    
  • 相关阅读:
    骑行318、 2016.7.22
    骑行318、 2016.7.21
    自定义的cell上面有图片时,如果产生了重用,图片可能会错乱问题
    当前View的坐标相对其他View的位置坐标
    自定义UIButton 实现图片和文字 之间距离和不同样式
    自定义导航栏 标题视图 返回按钮
    IOS 隐藏tabBar
    ShareSDK集成遇到问题
    导航栏相关设置
    根据字符内容计算宽高度
  • 原文地址:https://www.cnblogs.com/weiweivip666/p/15937193.html
Copyright © 2020-2023  润新知