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;
- SAutoRefPtr<IBitmap> image;
- 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日
|