是否有一个main.swift等同于@NSApplicationMain注释?

jam*_*her 6 cocoa nsapplication swift

在XCode中创建一个新的Cocoa项目给了我一个如下所示的AppDelegate.swift文件:

import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!
}
Run Code Online (Sandbox Code Playgroud)

@NSApplicationMain属性在此处记录

NSApplicationMain

将此属性应用于类以指示它是应用程序委托.使用此属性等同于调用该NSApplicationMain(_:_:)函数.

如果您不使用此属性,请提供一个main.swift顶层代码调用该NSApplicationMain(_:_:)函数的文件,如下所示:

import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
Run Code Online (Sandbox Code Playgroud)

文档中的说明不起作用:AppDelegate该类从未实例化.在这个答案中,vadian建议使用以下内容main.swift,它们比文档中的代码更好用:

import Cocoa
let appDelegate = AppDelegate()
NSApplication.shared().delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
Run Code Online (Sandbox Code Playgroud)

但是,这仍然没有提供相同的行为@NSApplicationMain.考虑使用以上main.swift内容AppDelegate.swift:

import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!
    var foo: NSStatusBar! = NSStatusBar.system();
}
Run Code Online (Sandbox Code Playgroud)

上面的内容AppDelegate.swift使用了@NSApplicationMain注释,但在使用上述内容时main.swift,它会在运行时因错误而失败

Assertion failed: (CGAtomicGet(&is_initialized)), function CGSConnectionByID, file Services/Connection/CGSConnection.c, line 127.
Run Code Online (Sandbox Code Playgroud)

我认为这个is_initialized错误意味着@NSApplicationMain设置一些东西,以便在函数初始化之后AppDelegate实例化.这表明以下内容,它将委托初始化移动到调用之后:NSApplicationMainmain.swiftNSApplicationMain

import Cocoa
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
let appDelegate = AppDelegate()
NSApplication.shared().delegate = appDelegate
Run Code Online (Sandbox Code Playgroud)

然而,这不起作用或者,因为NSApplicationMain永远不会返回!以上main.swift相当于文档中的破坏建议,因为后两行是死代码.

因此,我认为必须有一些方法来传递对我的AppDelegate类的引用作为NSApplicationMain函数的参数,以便Cocoa可以进行初始化然后实例化我的AppDelegate类本身.但是,我认为没办法这样做.

是否有一个main.swift提供真正等同于@NSApplicationMain注释的行为?如果是这样,那是main.swift什么样的?如果没有,@NSApplicationMain实际上在做什么,以及如何修改它?

vad*_*ian 7

该文档假定有一个xib或storyboard AppDelegate通过Interface Builder中的对象(蓝色多维数据集)实例化该类.在这种情况下两者

  • main.swiftNSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

  • @NSApplicationMainAppDelegate课堂上

表现完全一样.

如果没有xib或storyboard,您负责初始化AppDelegate类,将其分配给NSApplication.shared.delegate并运行应用程序.您还必须考虑对象的外观顺序.例如,AppKit在调用NSApplication.shared启动应用程序之前,您无法初始化与之相关的对象.


例如,这种略微改变的语法

let app = NSApplication.shared
let appDelegate = AppDelegate()
app.delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
Run Code Online (Sandbox Code Playgroud)

您可以初始化在状态栏AppDelegate之外applicationDidFinishLaunching:

let statusItem = NSStatusBar.system().statusItem(withLength: -1)
Run Code Online (Sandbox Code Playgroud)

因为初始化类之前NSApplication.shared()调用应用程序.AppDelegate


Vla*_*lad 7

这是我在使用由 Xcode 应用程序模板生成的初始值的 Storyboard 时为了在没有@NSApplicationMain注释和功能的情况下运行应用程序而做的事情(与下面描述的相关的轻微修改)。NSApplicationMain(_, _)NSWindowControllerMain Menu

文件:AppConfig.swift (Swift 4)

struct AppConfig {

   static var applicationClass: NSApplication.Type {
      guard let principalClassName = Bundle.main.infoDictionary?["NSPrincipalClass"] as? String else {
         fatalError("Seems like `NSPrincipalClass` is missed in `Info.plist` file.")
      }
      guard let principalClass = NSClassFromString(principalClassName) as? NSApplication.Type else {
         fatalError("Unable to create `NSApplication` class for `\(principalClassName)`")
      }
      return principalClass
   }

   static var mainStoryboard: NSStoryboard {
      guard let mainStoryboardName = Bundle.main.infoDictionary?["NSMainStoryboardFile"] as? String else {
         fatalError("Seems like `NSMainStoryboardFile` is missed in `Info.plist` file.")
      }

      let storyboard = NSStoryboard(name: NSStoryboard.Name(mainStoryboardName), bundle: Bundle.main)
      return storyboard
   }

   static var mainMenu: NSNib {
      guard let nib = NSNib(nibNamed: NSNib.Name("MainMenu"), bundle: Bundle.main) else {
         fatalError("Resource `MainMenu.xib` is not found in the bundle `\(Bundle.main.bundlePath)`")
      }
      return nib
   }

   static var mainWindowController: NSWindowController {
      guard let wc = mainStoryboard.instantiateInitialController() as? NSWindowController else {
         fatalError("Initial controller is not `NSWindowController` in storyboard `\(mainStoryboard)`")
      }
      return wc
   }
}
Run Code Online (Sandbox Code Playgroud)

文件main.swift (Swift 4)

// Making NSApplication instance from `NSPrincipalClass` defined in `Info.plist`
let app = AppConfig.applicationClass.shared

// Configuring application as a regular (appearing in Dock and possibly having UI)
app.setActivationPolicy(.regular)

// Loading application menu from `MainMenu.xib` file.
// This will also assign property `NSApplication.mainMenu`.
AppConfig.mainMenu.instantiate(withOwner: app, topLevelObjects: nil)

// Loading initial window controller from `NSMainStoryboardFile` defined in `Info.plist`.
// Initial window accessible via property NSWindowController.window
let windowController = AppConfig.mainWindowController
windowController.window?.makeKeyAndOrderFront(nil)

app.activate(ignoringOtherApps: true)
app.run()
Run Code Online (Sandbox Code Playgroud)

关于MainMenu.xib文件的注意事项:

Xcode 应用程序模板创建故事板,Application Scene其中包含Main Menu. 目前似乎无法以编程方式Main MenuApplication Scene. 但是有 Xcode file template Main Menu,它创建MainMenu.xib文件,我们可以以编程方式加载。