golang 模拟defer准确的释放一个对象中的大量资源

更新记录:
2018-4-19:重写destroyer.RunDefer,用reflect包实现任意返回类型函数的调用

正文

golang 中的 defer 关键字可以帮助程序员准确的释放资源,但是仅限于一个函数中。
如果一个全局对象中存储了大量需要手动释放的资源,那么编写释放函数时就很容易漏掉一些释放函数。
本文就是提供一种方法,模拟 defer 的功能,确保准确无误的执行所有释放函数。

思路

使用 “container/list” 反向保存需要执行的释放函数列表,释放资源时顺序执行存储在该列表中的函数。

具体实现

第一步

我们用一个实例来说明如何实现,下面就创建一个专门释放资源的对象,该对象是一个struct,内部有一个destroy_list成员:

```
type Destroyer struct{
    destroy_list *list.List
}
```

第二步

添加一个初始化函数 Init,用于初始化 destroy_list 成员:

```
func (self *Destroyer)Init(){
    self.destroy_list = new(list.List)
    self.destroy_list.Init()
}
```

第三步

添加一个存储函数的成员函数,作用是把参数存储到 destroy_list 的头部:

```
func (self *Destroyer)AddDefer( f interface{} ){
    self.destroy_list.PushFront( f )
}
```

第四步

添加一个执行函数,作用是顺序执行存储在 destroy_list 中的所有函数:

```
func (self *Destroyer)RunDefer(){
    var in []reflect.Value
    var t reflect.Type
    var f reflect.Value
    for e:=self.destroy_list.Front();e!=nil;e=e.Next(){
        t = reflect.TypeOf(e.Value)
        if t!=nil{
            if t.Kind()==reflect.Func && t.NumIn()==0{
                f = reflect.ValueOf(e.Value)
                f.Call(in)
            }
        }
    }
}
```

使用限制

作为参数的函数不能带有参数。

用法

还是用一个实例来说明,下面我们声明了一个结构体,包含几个需要释放资源的成员变量:

```
type appWin struct{
    window *sdl.Window
    renderer *sdl.Renderer
    background Texture
    fire *mix.Music
}
//初始化函数
func (self *appWin)Init(){
    ......
}
```

作为变量使用

可以声明为一个成员变量或者外部变量,本例中声明为外部变量,下面怎样写初始化代码:

```
deferer := new(Destroyer)
//初始化函数
func (self *appWin)Init(){
    self.window = sdl.CreateWindow(...)
    deferer.AddDefer( self.window.Destroy )
    self.renderer = sdl.CreateRenderer(...)
    deferer.AddDefer( self.renderer.Destroy )
    self.background = sdl.CreateTexture(...)
    deferer.AddDefer( self.background.Destroy )
    self.fire = mix.LoadMUS(...)
    deferer.AddDefer( self.fire.Free )
}
```

当程序结束前,调用“runDefer”释放资源:

```
deferer.RunDefer()
```

使用的形式如下:

```
deferer := new(Destroyer)
type appWin struct{
    ...
}
...
func main(){
    app = new(appWin)
    deferer.Init()
    app.Init()
    defer deferer.RunDefer()
    ... //调用 app 的其他函数
}
```

组合进其他结构体内使用

把“Destroyer”组合到新的结构体中,就可以在新的结构体中直接调用它的成员变量和函数。代码如下:

```
type appWin struct{
    Destroyer
    window *sdl.Window
    renderer *sdl.Renderer
    background Texture
    fire *mix.Music
}
//初始化函数
func (self *appWin)Init(){
    self.Destroyer.Init()
    self.window = sdl.CreateWindow(...)
    self.AddDefer( self.window.Destroy )
    self.renderer = sdl.CreateRenderer(...)
    self.AddDefer( self.renderer.Destroy )
    self.background = sdl.CreateTexture(...)
    self.AddDefer( self.background.Destroy )
    self.fire = mix.LoadMUS(...)
    self.AddDefer( self.fire.Free )
}
```

使用的形式如下:

```
func main(){
    app = new(appWin)
    app.Init()
    defer app.RunDefer()
    ... //调用 app 的其他函数
}
```

到此结束