基于metal-cpp实现NSThread时遇selector为空的崩溃问题求助
问题:metal-cpp自定义NSThread调用崩溃,报错target不实现selector((null))
我在基于metal-cpp封装应用实现自定义Cocoa主循环时,需要调用类似Objective-C的[NSThread detachNewThreadSelector:@selector(doNothing:) toTarget:stubTarget withObject:nil];逻辑,但metal-cpp没有NSThread类,所以自行实现了该类,代码如下:
自定义NSThread实现代码
metal-cpp/Foundation/NSThread.hpp
#pragma once #include <functional> #include "NSObject.hpp" namespace NS { class Runnable { public: virtual ~Runnable() {} virtual void run() {} }; class Thread: public NS::Referencing< Thread > { public: static void detachNewThread( const Runnable* pRunnable ); }; _NS_INLINE void NS::Thread::detachNewThread( const Runnable* pRunnable ) { NS::Value* pWrapper = NS::Value::value( pRunnable ); typedef void (*DispatchFunction)( NS::Value*, SEL, void* ); DispatchFunction run = []( Value* pSelf, SEL, void* unused ) { auto pDel = reinterpret_cast< NS::Runnable* >( pSelf->pointerValue() ); pDel->run(); }; return Object::sendMessage<void>(_NS_PRIVATE_CLS(NSThread), _NS_PRIVATE_SEL(detachNewThreadSelector_toTarget_withObject_), run, pWrapper, nullptr ); } }
私有选择器注册(metal-cpp/Foundation/NSPrivate.hpp)
_NS_PRIVATE_DEF_SEL(detachNewThreadSelector_toTarget_withObject_, "detachNewThreadSelector:toTarget:withObject:");
同时已将NSThread引入主头文件metal-cpp/Foundation/Foundation.hpp。
应用调用代码
class MainLoopTarget: public NS::Runnable { public: void run() override { std::cout << "run()" << std::endl; } }; auto target = new MainLoopTarget(); NS::AutoreleasePool* autorelease_pool = NS::AutoreleasePool::alloc()->init(); NS::Thread::detachNewThread(target); autorelease_pool->release(); delete target;
崩溃报错信息
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSThread initWithTarget:selector:object:]: target does not implement selector ((null))' *** First throw call stack: ( 0 CoreFoundation 0x00000001856aeccc __exceptionPreprocess + 176 1 libobjc.A.dylib 0x0000000185196788 objc_exception_throw + 60 2 Foundation 0x00000001867641d0 -[NSThread setQualityOfService:] + 0 3 Foundation 0x00000001867c1118 +[NSThread detachNewThreadSelector:toTarget:withObject:] + 60 4 libb3engine.dylib 0x000000010506bb88 _ZN2b38platform6darwin9DarwinApp5startEv + 336 5 B3 0x0000000104b7dd4c main + 252 6 dyld 0x00000001851d20e0 start + 2360 ) libc++abi: terminating due to uncaught exception of type NSException
已知问题出在类方法参数传递,但Objective-C基础薄弱,无法定位具体问题,求解决。
解决方案
崩溃的核心原因是对detachNewThreadSelector:toTarget:withObject:的参数传递逻辑完全错误:
参数顺序与类型不匹配
Objective-C的+[NSThread detachNewThreadSelector:toTarget:withObject:]三个参数的要求是:- 第一个参数:SEL选择器,必须是目标对象能响应的实例方法(签名为
- (void)selectorName:(id)object;) - 第二个参数:Objective-C对象实例(NSObject子类),要能响应上述选择器
- 第三个参数:传递给选择器的参数
你现在传递的第一个参数是
run(一个C++函数指针),不是有效的SEL,导致系统认为选择器是null,进而报错目标对象不实现该选择器。- 第一个参数:SEL选择器,必须是目标对象能响应的实例方法(签名为
C++对象无法直接作为Objective-C目标
你封装的NS::Runnable是纯C++类,不是NSObject子类,无法被Objective-C运行时识别为有效的target对象。
修正后的实现方案
1. 调整NSThread.hpp,使用Objective-C兼容的目标对象
#pragma once #include "NSObject.hpp" namespace NS { class Runnable : public NS::Object { public: virtual ~Runnable() {} virtual void run() = 0; // 定义Objective-C兼容的选择器方法 static void runSelector(NS::Object* pSelf, SEL, void*); }; class Thread: public NS::Referencing<Thread> { public: static void detachNewThread(Runnable* pRunnable); }; _NS_INLINE void NS::Runnable::runSelector(NS::Object* pSelf, SEL, void*) { auto pRunnable = static_cast<Runnable*>(pSelf); pRunnable->run(); } _NS_INLINE void NS::Thread::detachNewThread(Runnable* pRunnable) { // 注册选择器(确保在运行时存在) SEL runSel = NS::Selector::registerName("runSelector:"); // 调用Objective-C的NSThread方法,参数顺序正确:选择器、target、参数 Object::sendMessage<void>( _NS_PRIVATE_CLS(NSThread), _NS_PRIVATE_SEL(detachNewThreadSelector_toTarget_withObject_), runSel, pRunnable, nullptr ); } }
2. 应用调用代码调整(遵循metal-cpp内存管理规则)
class MainLoopTarget: public NS::Runnable { public: void run() override { std::cout << "run()" << std::endl; // 线程结束后释放自身 release(); } }; NS::AutoreleasePool* autorelease_pool = NS::AutoreleasePool::alloc()->init(); // 使用metal-cpp的alloc/init创建对象,避免直接delete MainLoopTarget* target = MainLoopTarget::alloc()->init(); NS::Thread::detachNewThread(target); autorelease_pool->release();
关键修改点说明
- 让
Runnable继承自NS::Object,使其成为Objective-C兼容对象 - 定义
runSelector:这个符合Objective-C方法签名的静态方法,作为线程调用的入口 - 传递正确的SEL选择器作为第一个参数,而非C++函数指针
- 遵循metal-cpp的内存管理规则,使用
alloc/init和release管理对象,避免直接用delete销毁Objective-C兼容对象
内容的提问来源于stack exchange,提问作者Denis Steinman
相关产品推荐
相关产品推荐

