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

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

这是一个最小的可重现示例,如果阅读时间太长,请转到下一部分有问题的部分,然后在需要时探索代码

最小示例:

假设一个简单的 C++ 命令行:

main.cpp

#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 .
$ 打开 ocapp.xcodeproj

问题:

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

应用完成启动
停止
.....
杀死:9

使用 CFRunLoopRun()CFRunLoopStop(CFRunLoopGetCurrent()) 时,它可以正确启动/停止,但从未触发 applicationDidFinishLaunching

停止
终止

问题:

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

解决方法

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

[NSApplication sharedApplication];
[NSApp run];

[NSApp stop:self];

是正确的。

罪魁祸首是您的CMakeLists.txt。您包含的那个创建了一个可执行的二进制文件。这对于控制台应用程序来说很好,但它不是由 AppName.app 文件夹和一堆其他文件组成的有效 MacOS 应用程序。由于您使用的 AppKit API 没有适当的 MacOS 应用程序框架,因此无法正常工作。

CMakeLists.txt 中的最低限度修复是:

add_executable(
    ${PROJECT_NAME}
    MACOSX_BUNDLE
)

现在您将在 Xcode 中拥有正确的 App 目标。您可以在 Internet 上查找适用于 MacOS 应用程序的 CMakeLists.txt 的更高级示例。

更新
所以我进一步调查并检查了
中的退出例程 -[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 的关键引述是:

似乎UI循环停止请求只在一个UI之后处理 事件(所以不仅仅是在主循环事件之后)。

事实确实如此。如果在“裸”可执行变体中我们添加

- (void) shutdown:(NSNotification *) notif
{
    NSLog(@"Stopping");
    //CFRunLoopStop(CFRunLoopGetCurrent());
    [NSApp stop:self];
    [NSApp abortModal]; //this is used for generating a UI NSEvent
}

我们得到所需的行为,应用程序正常终止。因此,您的“裸”应用变体不是正确的 MacOS 应用,因此它不会接收 UI 事件(无论如何它的 runloop 都可以正常工作)。

另一方面,MacOS 需要使用带有 Info.plist 等的适当 MacOS 应用程序包来设置应用程序窗口、Dock 图标等。

从长远来看,如果您根本不需要 AppKit,我确实建议您选择纯控制台应用程序,或者按本书做事。否则你会遇到这样的异常。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?