gpt4 book ai didi

objective-c++ - 如何 stop() 主线程 NSApp run() 事件循环? (嵌入在 C++ 中)

转载 作者:行者123 更新时间:2023-12-04 01:09:12 27 4
gpt4 key购买 nike

这是一个最小的可复制示例,如果阅读时间太长,请转到问题所在的下一部分,然后在需要时探索代码。
最小的例子:
假设一个简单的 C++ 命令行:
主程序

#include <iostream>
#include "Wrapper.h"
int main()
{
Wrapper wrapper;
wrapper.run();
std::cout << "Exiting" << std::endl;
}
Objective-C 包装头文件:Wrapper.h
struct OCWrapper;
class Wrapper
{
public:
Wrapper() noexcept;
virtual ~Wrapper() noexcept;
void run();
private:
OCWrapper* impl=nullptr;
};
它的实现:Wrapper.mm
#import "Wrapper.h"
#import "MyOCApp.h"

struct OCWrapper
{
MyOCApp* wrapped=nullptr;
};

Wrapper::Wrapper() noexcept: impl(new OCWrapper)
{
impl->wrapped = [[ MyOCApp alloc] init];
}

Wrapper::~Wrapper() noexcept
{
[impl->wrapped release];
delete impl;
}

void Wrapper::run()
{
[impl->wrapped run];
}
最后是有趣的部分,在 Objective-C 中,MyOCApp.h:
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>

@interface MyOCApp: NSObject
@end

@implementation MyOCApp
- (id)init
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationDidFinishLaunching:)
name:NSApplicationDidFinishLaunchingNotification object:nil];

return self;
}

- (void)run
{
[self performSelector:@selector(shutdown:) withObject:nil afterDelay: 2];
//CFRunLoopRun();

[NSApplication sharedApplication];
[NSApp run];
}

- (void) shutdown:(NSNotification *) notif
{
NSLog(@"Stopping");
//CFRunLoopStop(CFRunLoopGetCurrent());
[NSApp stop:self];
}

- (void) applicationDidFinishLaunching:(NSNotification *) notif
{
NSLog(@"Application ready");
}
@end
CMakeLists.txt
cmake_minimum_required (VERSION 3.10.0)
cmake_policy( SET CMP0076 NEW)

set(CMAKE_CXX_STANDARD 17)

project(ocapp)

add_executable(${PROJECT_NAME})

find_library(APP_KIT AppKit)
find_library(CORE_FOUNDATION CoreFoundation)
target_link_libraries( ${PROJECT_NAME} ${APP_KIT} ${CORE_FOUNDATION} )

target_sources( ${PROJECT_NAME} PRIVATE "main.cpp" "Wrapper.mm" PUBLIC "Wrapper.h" "MyOCApp.h" )
可以使用以下命令构建项目:

$ cmake -G Xcode .
$ open ocapp.xcodeproj


问题:
使用时 [NSApp run][NSApp stop:self] ,我无法停止事件循环,因此它无限期地继续运行。

Application finished launching
Stopping
.....
Killed: 9


使用时 CFRunLoopRun()CFRunLoopStop(CFRunLoopGetCurrent()) ,它正确启动/停止,但是 applicationDidFinishLaunching永远不会被触发。

Stopping
Terminating


问题:
为什么是这样?以及如何让这两个功能正常工作?

最佳答案

问题不在附加的代码中。您现有的变体

[NSApplication sharedApplication];
[NSApp run];
[NSApp stop:self];
是正确的。
罪魁祸首是你的 CMakeLists.txt .您包含的那个创建了一个可执行的二进制文件。这对于控制台应用程序来说很好,但它不是由 AppName.app 文件夹和一堆其他文件组成的有效 MacOS 应用程序。由于您在没有 MacOS 应用程序的适当脚手架的情况下使用 AppKit API,因此它不起作用。
您的 CMakeLists.txt 中的最低限度修复是:
add_executable(
${PROJECT_NAME}
MACOSX_BUNDLE
)
现在您将在 Xcode 中拥有正确的 App 目标。您可以查找 CMakeLists.txt 的更高级示例。适用于 Internet 上的 MacOS 应用程序。
更新
所以我进一步调查并检查了退出程序 -[NSApplication run] ( +[NSApp run] 是为了兼容性而留下的同义词,但真正的实现是在 -[NSApplication run] 中)。
我们可以像这样通过 lldb 设置一个符号断点: b "-[NSApplication run]"感兴趣的片段(对于 X86-64)是:
->  0x7fff4f5f96ff <+1074>: add    rsp, 0x98
0x7fff4f5f9706 <+1081>: pop rbx
0x7fff4f5f9707 <+1082>: pop r12
0x7fff4f5f9709 <+1084>: pop r13
0x7fff4f5f970b <+1086>: pop r14
0x7fff4f5f970d <+1088>: pop r15
0x7fff4f5f970f <+1090>: pop rbp
0x7fff4f5f9710 <+1091>: ret
我们可以验证仅在捆绑变体中而不是在“裸”可执行变体中击中箭头指向的断点。经过进一步研究,我找到了这个答案 https://stackoverflow.com/a/48064763/5329717这是非常有帮助的。 @Remko 的关键引述是:

it seems that the UI loop stop request is only processed after a UIevent (so not just after a main loop event).


情况确实如此。如果在“裸”可执行变体中我们添加
- (void) shutdown:(NSNotification *) notif
{
NSLog(@"Stopping");
//CFRunLoopStop(CFRunLoopGetCurrent());
[NSApp stop:self];
[NSApp abortModal]; //this is used for generating a UI NSEvent
}
我们得到所需的行为,应用程序正常终止。因此,您的“裸”应用程序变体不是正确的 MacOS 应用程序,因此它不会接收 UI 事件(无论如何它的 runloop 都可以正常工作)。
另一方面,使用 Info.plist 拥有合适的 MacOS 应用程序包MacOS 需要等来设置应用程序窗口、Dock 图标等。
从长远来看,如果您根本不需要 AppKit,我确实建议您选择纯控制台应用程序,或者按本书做事。否则你会遇到这样的异常。

关于objective-c++ - 如何 stop() 主线程 NSApp run() 事件循环? (嵌入在 C++ 中),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65393619/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com