Nasıl durdurulur () ana iş parçacığı NSApp run () olay döngüsü? (C ++ içine yerleştirilmiştir)

Dec 21 2020

Burada yeniden üretilebilir minimum bir örnek, okunamayacak kadar uzunsa, problemin olduğu bir sonraki bölüme gidin ve gerekirse kodu inceleyin.

Minimal örnek:

Basit bir C ++ komut satırı varsayalım:

main.cpp

#include <iostream>
#include "Wrapper.h"
int main()
{
    Wrapper wrapper;
    wrapper.run();
    std::cout << "Exiting" << std::endl;
}

Objective-C sarmalayıcı başlığı: Wrapper.h

struct OCWrapper;
class Wrapper
{
public:
    Wrapper() noexcept;
    virtual ~Wrapper() noexcept;
    void run();
private:
    OCWrapper* impl=nullptr;
};

Ve uygulaması: 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];
}

Ve son olarak ilginç kısım, 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" )

Proje aşağıdaki komutlarla oluşturulabilir:

$ cmake -G Xcode.
$ open ocapp.xcodeproj

Sorun:

Kullanırken [NSApp run]ve [NSApp stop:self]ben olay döngü durdurmak alamıyorum, bu yüzden süresiz yayınlanmaya devam.

Uygulama başlatmayı tamamladı
Durduruluyor
.....
Öldürüldü: 9

Ne zaman kullanarak CFRunLoopRun()ve CFRunLoopStop(CFRunLoopGetCurrent())bunu başlatmak / durdurmak doğru ama applicationDidFinishLaunchingtetiklenir asla.


Sonlandırmayı Durdurma

Soru:

Bu neden? ve her ikisi de nasıl çalışır?

Yanıtlar

2 Kamil.S Jan 06 2021 at 03:56

Sorun ekteki kodda değil. Mevcut varyantınız

[NSApplication sharedApplication];
[NSApp run];

ve

[NSApp stop:self];

doğru.

Suçlu senin CMakeLists.txt. Eklediğiniz, çalıştırılabilir bir ikili dosya oluşturur. Bir konsol uygulaması için sorun değil, ancak AppName.app klasörü ve diğer dosyalardan oluşan geçerli bir MacOS uygulaması değil. Bir MacOS uygulamasının uygun iskelesi olmadan AppKit API kullandığınız için çalışmaz.

Bir minimum düzeltme sizin de CMakeLists.txtgeçerli:

add_executable(
    ${PROJECT_NAME}
    MACOSX_BUNDLE
)

Şimdi Xcode'da doğru bir Uygulama hedefine sahip olacaksınız. CMakeLists.txtİnternette MacOS uygulamaları için uygun olan daha gelişmiş örneklere bakabilirsiniz .

Güncelleme
Bu yüzden daha fazla araştırdım ve çıkış rutinini inceledim
-[NSApplication run]( +[NSApp run]uyumluluk için bir eşanlamlı ama gerçek uygulama içeride -[NSApplication run]). Lldb aracılığıyla sembolik bir kesme noktası belirleyebiliriz: b "-[NSApplication run]"ilgi parçacığı ( X86-64 için):

->  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  

Ok noktalarının yalnızca paketlenmiş varyantta vurulduğu, ancak "çıplak" yürütülebilir varyantta vurulmadığı bir kesme noktasını doğrulayabiliriz. Daha fazla araştırmadan sonra bu cevabı buldumhttps://stackoverflow.com/a/48064763/5329717bu çok faydalıdır. @Remko'nun anahtar sözü:

UI döngüsü durdurma isteğinin yalnızca bir UI olayından sonra işlendiği görülmektedir (yani, yalnızca bir ana döngü olayından sonra değil).

Ve bu gerçekten de böyledir. "Çıplak" yürütülebilir varyantta eklersek

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

İstenilen davranışı elde ederiz ve uygulama normal şekilde sona erer. Yani "çıplak" uygulama varyantınız doğru bir MacOS uygulaması değildir, dolayısıyla UI olaylarını almaz (runloop'u ne olursa olsun doğru çalışır).

Öte yandan, MacOS'un Info.plistbir uygulama penceresi, Dock simgesi vb. Kurması için uygun MacOS uygulama paketine vb. Sahip olmak gerekir.

Uzun vadede, AppKit'e hiç ihtiyacınız yoksa ya saf konsol uygulamasına gitmenizi ya da kitapta bir şeyler yapmanızı öneririm. Aksi takdirde bu tür anormalliklerle karşılaşırsınız.