Apple Metal API对象析构问题:析构函数未执行原因排查
问题
运行Apple官方提供的metal-cpp示例代码时,能正常看到构造函数的打印输出,但始终看不到析构函数的打印内容。进一步测试发现,pSharedApp->run()之后的所有代码都没有执行——即使关闭应用窗口,程序退出码为0,断点和自定义打印语句都无响应。
简化后的代码如下:
#include <iostream> #define NS_PRIVATE_IMPLEMENTATION #define MTL_PRIVATE_IMPLEMENTATION #define MTK_PRIVATE_IMPLEMENTATION #define CA_PRIVATE_IMPLEMENTATION #include <Metal/Metal.hpp> #include <MetalKit/MetalKit.hpp> class Renderer { public: explicit Renderer( MTL::Device* pDevice ); ~Renderer(); void draw( MTK::View* pView ); private: MTL::Device* _pDevice = nullptr; MTL::CommandQueue* _pCmdQueue = nullptr; }; class AppView final : public MTK::ViewDelegate { public: explicit AppView( MTL::Device* pDevice ); ~AppView() override; void drawInMTKView( MTK::View* pView ) override; private: Renderer* _pRenderer = nullptr; }; class AppDelegate final : public NS::ApplicationDelegate { public: ~AppDelegate() override; static NS::Menu* create_menu_bar(); void applicationWillFinishLaunching( NS::Notification* pNotification ) override; void applicationDidFinishLaunching( NS::Notification* pNotification ) override; bool applicationShouldTerminateAfterLastWindowClosed( NS::Application* pSender ) override; private: NS::Window* _pWindow = nullptr; MTK::View* _pView = nullptr; MTL::Device* _pDevice = nullptr; AppView* _pAppView = nullptr; }; int main() { auto* pool = NS::AutoreleasePool::alloc()->init(); AppDelegate appDelegate; NS::Application* pSharedApp = NS::Application::sharedApplication(); pSharedApp->setDelegate( &appDelegate ); pSharedApp->run(); pool->release(); return 0; } // ----------------------------------------------- AppView -------------------------------------------------------------------------------- AppView::AppView( MTL::Device* pDevice ) : MTK::ViewDelegate() , _pRenderer( new Renderer( pDevice ) ) { std::cout << "AppView Instantiated. \n"; } AppView::~AppView() { delete _pRenderer; std::cout << "AppView Destroyed. \n";} void AppView::drawInMTKView( MTK::View* pView ) { _pRenderer->draw( pView ); } // ----------------------------------------------- Renderer -------------------------------------------------------------------------------- Renderer::Renderer( MTL::Device* pDevice ) : _pDevice( pDevice->retain() ) { _pCmdQueue = _pDevice->newCommandQueue(); std::cout << "Renderer Instantiated. \n";} Renderer::~Renderer() { std::cout << "Renderer Destroyed. \n"; _pCmdQueue->release(); _pDevice->release(); } // ----------------------------------------------- AppDelegate -------------------------------------------------------------------------------- AppDelegate::~AppDelegate() { _pWindow->release(); _pView->release(); _pDevice->release(); delete _pAppView; } void AppDelegate::applicationWillFinishLaunching( NS::Notification* pNotification ) { NS::Menu* pMenu = create_menu_bar(); NS::Application* pApp = reinterpret_cast< NS::Application* >( pNotification->object() ); pApp->setMainMenu( pMenu ); pApp->setActivationPolicy( NS::ActivationPolicy::ActivationPolicyRegular ); } void AppDelegate::applicationDidFinishLaunching( NS::Notification* pNotification ) { CGRect frame = (CGRect) { {260.f, 180.f}, {1200.f, 750.f} }; _pWindow = NS::Window::alloc()->init( frame, NS::WindowStyleMaskClosable | NS::WindowStyleMaskTitled, NS::BackingStoreBuffered, false ); _pDevice = MTL::CreateSystemDefaultDevice(); _pView = MTK::View::alloc()->init( frame, _pDevice ); _pView->setColorPixelFormat( MTL::PixelFormat::PixelFormatBGRA8Unorm_sRGB ); _pView->setClearColor( MTL::ClearColor::Make( 0.01, 0.01, 0.01, 1.0 ) ); _pAppView = new AppView( _pDevice ); _pView->setDelegate( _pAppView ); _pWindow->setContentView( _pView ); _pWindow->setTitle( NS::String::string( "00 - Window", NS::StringEncoding::UTF8StringEncoding ) ); _pWindow->makeKeyAndOrderFront( nullptr ); auto* pApp = reinterpret_cast< NS::Application* >( pNotification->object() ); pApp->activateIgnoringOtherApps( true ); } bool AppDelegate::applicationShouldTerminateAfterLastWindowClosed( NS::Application* pSender ) { return true; }
原因与解决方法
核心原因
Cocoa应用的NSApplication::run()默认在应用终止时直接调用exit(),导致进程直接退出,不会执行run()之后的代码,栈上的局部变量(比如main里的AppDelegate appDelegate)无法触发析构函数,进而AppView和Renderer的析构逻辑也不会被执行。
解决步骤
1. 在AppDelegate中添加终止回调处理资源清理
重写applicationWillTerminate方法,手动释放动态分配的资源,确保析构函数被调用:
void AppDelegate::applicationWillTerminate(NS::Notification* pNotification) { // 手动触发AppView和Renderer的析构 delete _pAppView; _pAppView = nullptr; // 释放Cocoa对象 _pWindow->release(); _pWindow = nullptr; _pView->release(); _pView = nullptr; _pDevice->release(); _pDevice = nullptr; }
同时修改AppDelegate的析构函数,避免重复释放已经清理过的资源:
AppDelegate::~AppDelegate() { // 这里不再处理已经在applicationWillTerminate中释放的资源 }
2. 调整main函数中AppDelegate的存储方式
将AppDelegate改为堆分配,确保在run()之后可以手动触发其析构:
int main() { auto* pool = NS::AutoreleasePool::alloc()->init(); AppDelegate* pAppDelegate = new AppDelegate(); NS::Application* pSharedApp = NS::Application::sharedApplication(); pSharedApp->setDelegate( pAppDelegate ); pSharedApp->run(); // 手动释放AppDelegate,触发其析构逻辑 delete pAppDelegate; pool->release(); return 0; }
额外提示
- Cocoa应用的事件循环终止后直接退出进程是设计决定,不要依赖栈变量的析构处理关键资源清理。
- 所有必须执行的清理逻辑,优先放在
applicationWillTerminate等生命周期回调中,或使用智能指针管理C++对象的生命周期。
内容的提问来源于stack exchange,提问作者rosxnb
相关产品推荐
相关产品推荐

