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(){ for e:=self.destroy_list.Front();e!=nil;e=e.Next(){ switch f := e.Value.(type){ case func(): f() case func() error: f() } } } ```
该对象的限制
上述函数允许执行的函数类型限制为两种:
- “func()”:无参数,无返回值
- “func() error”:无参数,返回类型是“error”
如果程序需要增加其他返回类型的释放函数,可以根据具体需要在“switch”代码中添加,但是都不允许含有参数,例如:
``` switch f := e.Value.(type){ case func(): f() case func() error: f() case func() int: f() case func() bool: f() } ```
但是经过我的测试,不能用“default”,否则“f”的类型还是“interface{}”,不能作为函数执行。
用法
还是用一个实例来说明,下面我们声明了一个结构体,包含几个需要释放资源的成员变量:
``` 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) 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 的其他函数 } ```
到此结束