在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中,vadian为main.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
实际上在做什么,以及如何修改它?
答案 0 :(得分:6)
文档假定有一个xib或storyboard通过Interface Builder中的对象(蓝色多维数据集)实例化AppDelegate
类。在这种情况下都是
main.swift
包含NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
和
@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项目应用程序之间的行为有任何差异,请告诉我。)