メインスレッドNSApprun()イベントループをstop()する方法は?(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 -GXcode。
$ open ocapp.xcodeproj
問題:
とを使用する[NSApp run]
と[NSApp stop:self]
、イベントループを停止できないため、無期限に実行され続けます。
アプリケーションの起動が終了しました
停止中
.....
強制終了:9
とを使用するCFRunLoopRun()
とCFRunLoopStop(CFRunLoopGetCurrent())
、正しく開始/停止しますが、applicationDidFinishLaunching
トリガーされることはありません。
終了の停止
質問:
どうしてこれなの?両方の機能を機能させる方法は?
回答
問題は添付のコードにはありません。既存のバリアント
[NSApplication sharedApplication];
[NSApp run];
そして
[NSApp stop:self];
正しい。
犯人はあなたCMakeLists.txt
です。含めたものは実行可能バイナリを作成します。これはコンソールアプリには問題ありませんが、AppName.appフォルダーと他のファイルの束で構成される有効なMacOSアプリではありません。MacOSアプリの適切なスキャフォールドなしでAppKitAPIを使用しているため、機能しません。
最低限の修正、あなたの中CMakeLists.txt
です。
add_executable(
${PROJECT_NAME}
MACOSX_BUNDLE
)
これで、Xcodeに正しいアプリターゲットが作成されます。CMakeLists.txt
インターネット上の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による重要な引用は次のとおりです。
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は関係なく正しく機能します)。
一方、Info.plist
MacOSがアプリウィンドウやDockアイコンなどを設定するには、適切なMacOSアプリバンドルなどが必要です。
長期的には、AppKitがまったく必要ない場合は、純粋なコンソールアプリを使用するか、本で何かを行うことをお勧めします。そうでなければ、あなたはそのような異常に遭遇するでしょう。