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

时间:2017-03-18 14:42:12

标签: swift cocoa nsapplication

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

import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!
}

The @NSApplicationMain attribute is documented here

  

NSApplicationMain

     

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

     

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

import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

文档中的说明不起作用:AppDelegate类永远不会被实例化。在this answer中,vadianmain.swift建议了以下内容,这些内容比文档中的代码更好

import Cocoa
let appDelegate = AppDelegate()
NSApplication.shared().delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

但是,这仍然不会提供与@NSApplicationMain相同的行为。考虑将以上main.swift与以下AppDelegate.swift

一起使用
import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var window: NSWindow!
    var foo: NSStatusBar! = NSStatusBar.system();
}

上述AppDelegate.swift适用于@NSApplicationMain注释,但在使用上述main.swift时,它会在运行时因错误而失败

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

我认为此is_initialized错误意味着@NSApplicationMain设置了一些内容,以便在AppDelegate函数初始化之后NSApplicationMain实例化 。这表明以下main.swift,它将委托初始化移至NSApplicationMain调用之后:

import Cocoa
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
let appDelegate = AppDelegate()
NSApplication.shared().delegate = appDelegate

但是,这 ,因为NSApplicationMain永远不会返回!上面的main.swift等同于文档中的破坏建议,因为后两行是死代码。

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

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

3 个答案:

答案 0 :(得分:6)

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

  • main.swift包含NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

    {li> @NSApplicationMain AppDelegate

表现完全相同。

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

例如,使用稍微改变的语法

let app = NSApplication.shared()
let appDelegate = AppDelegate()
app.delegate = appDelegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

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

let statusItem = NSStatusBar.system().statusItem(withLength: -1)

因为启动应用的NSApplication.shared()在初始化AppDelegate类之前被称为

答案 1 :(得分:3)

以下是我在使用带有Xcode应用程序模板生成的初始@NSApplicationMain的Storyboard时运行没有NSApplicationMain(_, _)注释和函数NSWindowController的应用程序所做的事情(略微修改与{{{ 1}}如下所述。)

文件: AppConfig.swift (Swift 4)

Main Menu

档案 main.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
   }
}

关于// 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() 档案的说明:

Xcode应用程序模板使用MainMenu.xib创建包含Application Scene的故事板。目前似乎无法以编程方式从Main Menu加载Main Menu。但是有Xcode文件模板Application Scene,它创建了Main Menu文件,我们可以通过编程方式加载。

答案 2 :(得分:0)

使用以下AppDelegate.swift替换默认的Cocoa项目main.swift。该应用程序的行为与以前相同。因此,以下代码提供了@NSApplicationMain注释的语义。

import Cocoa
class AppDelegate: NSObject, NSApplicationDelegate { }
let myApp: NSApplication = NSApplication.shared()
let myDelegate: AppDelegate = AppDelegate()
myApp.delegate = myDelegate
let mainBundle: Bundle = Bundle.main
let mainNibFileBaseName: String = mainBundle.infoDictionary!["NSMainNibFile"] as! String
mainBundle.loadNibNamed(mainNibFileBaseName, owner: myApp, topLevelObjects: nil)
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

(我在vadian's answer的帮助下构建了这个。如果上面和默认的Cocoa项目应用程序之间的行为有任何差异,请告诉我。)