C++程序员最难的一环就是处理内存泄漏。
很多情况下,一个对象在一个模块里分配了内存,忘记了释放,或者在另一个模块里释放都会导致内存相关的问题。
SOUI中大部分暴露在应用层的对象都使用类似COM的引用计数来管理对象的生命周期,包含SWindow, ISkin, EventArg, SStringT, IRenderTarget, IBitmap, IAdapter以及各种扩展组件。
SOUI中引用计数的基类是:SOUI::IObfRef
namespace SOUI { struct IObjRef { virtual long AddRef() PURE; virtual long Release() PURE; virtual void OnFinalRelease() PURE; }; }
SOUI中使用引用计数有一个简单的原则:一个对象,谁AddRef,那也应该它来Release。
经常有人问:为什么我调用了SImageWnd::SetImage就有内存泄漏了?
用户的代码可能是下面这样的:
bool SDemoSkin::SetImage(SStringW imgfile) { m_bIsColor = false; m_FilePath = imgfile; IBitmap *image = LOADIMAGE2(L"file:" + imgfile); if (image) { SetImage(image); //image->Release(); return true; } return false; }
这个问题很简单,看一下SetImage(IBitmap*)这个方法的代码就知道,它会自己持有这个IBitmap*对象。
通常这是用户从文件或者内存加载后创建的IBitmap对象,所有SOUI对象的创建都自动调用了AddRef,因此调用它个方法的人调用完成后,不再使用这个对象,则应该相应的调用一下Release(即打开代码中被注释的行)。
SOUI中也提供了一个智能指针来简化这个引用计数的操作:SAutoRefPtr (2.x是CAutoRefPtr)
使用智能指针,上面代码可以改写为:
bool SDemoSkin::SetImage(SStringW imgfile) { m_bIsColor = false; m_FilePath = imgfile; SAutoRefPtrimage; image.Attach(LOADIMAGE2(L"file:" + imgfile)); if (image) { SetImage(image); return true; } return false; }
注:由于LOADIMAGE2内部已经调用了AddRef,而智能指针直接赋值时会自动AddRef,因此这里要用image.Attach来获得指针。Attach不会自动调用AddRef.
使用好IObjRef,配合上SAutoRefPtr,可以将SOUI的内存泄漏问题降低很多。
启程软件 2019年10月17日