我尝试使用 metal-cpp(源代码也可以在 here)包装器为我的应用程序实现自定义 Cocoa 主循环。
第一步是写一些类似的内容:
[NSThread detachNewThreadSelector:@selector(doNothing:)
toTarget:stubTarget
withObject:nil];
运行存根选择器(解决方案基于 GLFW 源代码)。
metal-cpp不包含
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_SEL
变体
_NS_PRIVATE_DEF_SEL(detachNewThreadSelector_toTarget_withObject_,
"detachNewThreadSelector:toTarget:withObject:");
最后,我将
NSThread
纳入主标题 metal-cpp/Foundation/Foundation.hpp
。
NSThread
的源代码是在研究NSApplication
的源代码和Apple参考文献时编写的。我不了解 Objective-C,它的代码对我来说有点神奇。
后来,我在我的应用程序中调用
NSThread::detachNewThread()
:
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 技能还不足以了解哪里和什么。
好吧,我犯了错误。正确的实现如下:
在 metal-cpp/Foundation/NSPrivate.hpp 中:
_NS_PRIVATE_DEF_SEL(detachNewThreadSelector_toTarget_withObject_,
"detachNewThreadSelector:toTarget:withObject:");
_NS_PRIVATE_DEF_SEL(run_,
"run:");
在 metal-cpp/Foundation/NSThread.hpp:
_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();
};
// Register the class method on Objective-C side
class_addMethod( (Class)_NS_PRIVATE_CLS( NSValue ), _NS_PRIVATE_SEL( run_ ), (IMP)run, "v@:@" );
// Here we call an Objective-C side's selector
return Object::sendMessage<void>(_NS_PRIVATE_CLS(NSThread), _NS_PRIVATE_SEL(detachNewThreadSelector_toTarget_withObject_), _NS_PRIVATE_SEL(run_), pWrapper, nullptr );
}