我知道,我不是第一个尝试在 OSX 上将 Cocoa 与现有的 c/c++ 主循环一起使用的人,但我并不是很喜欢迄今为止遇到的解决方案,所以我想出了一个不同的解决方案我想讨论的想法。我发现(在 glut、glfw、SDL 以及我认为的 QT 中)最常见的方法是使用轮询来替换 NSApplications 运行方法并自己处理事件:
nextEventMatchingMask:untilDate:inMode:dequeue:
这有一个很大的缺点,就是 cpu 永远不会真正空闲,因为你必须一直轮询以检查是否有任何新事件,而且它不是 NSApplications 运行函数中唯一发生的事情,所以它可能会破坏一些如果您使用此替换,请详细说明。
所以我想做的是保持 cocoa runLoop 完好无损。想象一下,您将在 c++ 中实现自己的计时器方法,这些方法通常会在您的主循环中进行管理和触发(这只是一个示例)。我的想法是将所有循环部分移动到辅助线程(因为据我所知,需要从主线程调用 NSApplication 运行),然后将自定义事件发布到我的 NSApplication 派生版本,该版本在其内部适本地处理它们发送事件:方法。例如,如果我的计时器在我的 c++ 循环中触发,我会向 NSApplication 发布一个自定义事件,该事件反过来运行我的应用程序的 loopFunc() 函数(也位于主线程中),该函数会适本地将事件发送到我的 c++ 事件链中. 首先,您认为这是一个好的解决方案吗? 如果是,你将如何在 cocoa 中实现它,我只在 NSEvent Reference 中找到了这个方法来发布自定义 NSApplicationDefined 事件:
otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:
然后使用类似的东西:
[NSApp postEvent:atStart:]
通知 NSApplication。
我宁愿发布一个没有关于窗口的任何信息的事件(在 otherEventWithType 中),我可以直接忽略那部分吗?
然后我想覆盖类似这样的 NSApplications sendEvent 函数:
- (void)sendEvent:(NSEvent *)event
{
//this is my custom event that simply tells NSApplication
//that my app needs an update
if( [event type] == NSApplicationDefined)
{
myCppAppPtr->loopFunc(); //only iterates once
}
//transform cocoa events into my own input events
else if( [event type] == NSLeftMouseDown)
{
...
myCppAppPtr->loopFunc(); //also run the loopFunc to propagate input events
}
...
//dont break the cocoa event chain
[super sendEvent:event];
}
很抱歉这篇长文,但这一直困扰着我,因为到目前为止我对这个主题的发现并不满意。这就是我在 NSApplication 中发布和检查自定义事件的方式吗?您认为这是一种无需轮询即可将 cocoa 集成到现有运行循环中的有效方法吗?
最佳答案
好的,毕竟这花费了我比我预期更多的时间,我想概述一下我尝试过的事情,并告诉你我在这些事情上的经历。这有望为人们在未来将 Cocoa 集成到现有主循环中节省大量时间。我在搜索讨论的问题时发现的第一个函数是函数
nextEventMatchingMask:untilDate:inMode:dequeue:
但正如我在问题中所说,我的主要问题是我必须不断轮询新事件,这会浪费相当多的 CPU 时间。 所以我尝试了以下两种方法来简单地让我的 mainloops 更新函数从 NSApplications 主循环中调用:
向 NSApplication 发布自定义事件,覆盖 NSApplications
sendEvent: 函数并简单地调用我的 mainloops 更新函数
从那里。类似这样:
NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined
location: NSMakePoint(0,0)
modifierFlags: 0
timestamp: 0.0
windowNumber: 0
context: nil
subtype: 0
data1: 0
data2: 0];
[NSApp postEvent: event atStart: YES];
//the send event function of my overwritten NSApplication
- (void)sendEvent:(NSEvent *)event
{
//this is my custom event that simply tells NSApplication
//that my app needs an update
if( [event type] == NSApplicationDefined)
{
myCppAppPtr->loopFunc(); //only iterates once
}
}
这在理论上只是一个好主意,因为如果我的应用更新得非常 快速(例如由于计时器快速触发),整个 cocoa 事件队列变得完全没有响应,因为我添加了 许多自定义事件。 所以不要使用这个...
将 performSelectorOnMainThread 与 cocoaFunction 一起使用 轮流调用我的更新函数
[theAppNotifier
performSelectorOnMainThread:@selector(runMyMainLoop) withObject:nil
waitUntilDone:NO ];
这好多了,app 和 cocoa EventLoop 非常好 react 灵敏。如果您只是想实现一些简单的事情,我会 建议沿着这条路线走,因为它是最简单的路线 在这里提出。无论如何,我几乎无法控制顺序 这种方法发生的事情(如果你有一个 多线程应用程序),即当我的计时器被触发并且会做一个相当 长期工作,通常他们会在任何新的工作之前重新安排 鼠标/键盘输入可以添加到我的 eventQueue 中,因此会 使整个输入变得迟缓。在窗口上打开垂直同步 由重复计时器绘制的内容足以让这种情况发生。
毕竟我不得不回到 nextEventMatchingMask:untilDate:inMode:dequeue: 并且经过反复试验,我实际上找到了一种无需持续轮询即可使其工作的方法。我的循环结构是这样的:
void MyApp::loopFunc()
{
pollEvents();
processEventQueue();
updateWindows();
idle();
}
其中pollEvents和idle是重要的函数,基本上我用的是类似这个的。
void MyApp::pollEvents()
{
NSEvent * event;
do
{
event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES];
//Convert the cocoa events to something useful here and add them to your own event queue
[NSApp sendEvent: event];
}
while(event != nil);
}
为了在 idle() 函数中实现阻塞,我这样做了(不确定这是否好,但它似乎工作得很好!):
void MyApp::idle()
{
m_bIsIdle = true;
NSEvent * event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:NO];
m_bIsIdle = false;
}
这会导致 cocoa 等到有事件发生,如果发生这种情况,则空闲简单地退出并且 loopfunc 再次启动。如果我的一个计时器(我不使用 cocoa 计时器)触发,要唤醒空闲功能,我再次使用自定义事件:
void MyApp::wakeUp()
{
m_bIsIdle = false;
//this makes sure we wake up cocoas run loop
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSEvent* event = [NSEvent otherEventWithType: NSApplicationDefined
location: NSMakePoint(0,0)
modifierFlags: 0
timestamp: 0.0
windowNumber: 0
context: nil
subtype: 0
data1: 0
data2: 0];
[NSApp postEvent: event atStart: YES];
[pool release];
}
由于我随后立即清除了整个 cocoa 事件队列,因此我没有遇到 第 1 节中描述的相同问题。
但是,这种方法也有一些缺点,因为我认为它并没有完成 [NSApplication run] 在内部所做的所有事情,即应用程序委托(delegate)这样的事情:
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)theApplication
{
return YES;
}
似乎不起作用,无论如何我可以忍受,因为您可以轻松地检查自己是否刚刚关闭了最后一个窗口。
我知道这个答案很长,但我的旅程也是如此。我希望这可以帮助某人并防止人们犯我犯的错误。
关于c++ - Cocoa:将 NSApplication 集成到现有的 c++ 主循环中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6732400/
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
我一直很高兴地使用DelayedJob习惯用法:foo.send_later(:bar)这会调用DelayedJob进程中对象foo的方法bar。我一直在使用DaemonSpawn在我的服务器上启动DelayedJob进程。但是...如果foo抛出异常,Hoptoad不会捕获它。这是任何这些包中的错误...还是我需要更改某些配置...或者我是否需要在DS或DJ中插入一些异常处理来调用Hoptoad通知程序?回应下面的第一条评论。classDelayedJobWorker 最佳答案 尝试monkeypatchingDelayed::W
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon
我正在尝试为我的iOS应用程序设置cocoapods但是当我执行命令时:sudogemupdate--system我收到错误消息:当前已安装最新版本。中止。当我进入cocoapods的下一步时:sudogeminstallcocoapods我在MacOS10.8.5上遇到错误:ERROR:Errorinstallingcocoapods:cocoapods-trunkrequiresRubyversion>=2.0.0.我在MacOS10.9.4上尝试了同样的操作,但出现错误:ERROR:Couldnotfindavalidgem'cocoapods'(>=0),hereiswhy:U
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我
我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“
我正在创建一个新的Rails3.1应用程序。我希望这个新应用程序重用现有数据库(由以前的Rails2应用程序创建)。我创建了新的应用程序定义模型,它重用了数据库中的一些现有数据。在开发和测试阶段,一切正常,因为它在干净的表数据库上运行,但是当尝试部署到生产环境时,我收到如下消息:PGError:ERROR:column"email"ofrelation"users"alreadyexists***[err::localhost]:ALTERTABLE"users"ADDCOLUMN"email"charactervarying(255)DEFAULT''NOTNULL但是我在迁移中有这
我遇到了RVM的问题,所以我卸载并重新安装了它。事实是我实际上尝试过rbenv,但这对我来说没有用,所以我试图让rvm重新启动并运行-而不必安装重复版本的Ruby。我至少安装了1个现有版本的Ruby:ruby--versionruby1.8.7(2011-12-28patchlevel357)[universal-darwin11.0]但是当我执行rvmlist时,我得到一个空白列表:bash-3.2$rvmlistrvmrubies#Defaultrubynotset.Try'rvmaliascreatedefault'.#=>-current#=*-current&&default