【问题标题】:NSApplicationDelegate not working without StoryboardNSApplicationDelegate 在没有情节提要的情况下无法工作
【发布时间】:2016-10-12 20:51:40
【问题描述】:

我正在尝试在 macOS Sierra 上的 Xcode 8(稳定版)中创建一个没有情节提要的 macOS 应用程序。但是,我的AppDelegate 甚至没有被启动。这是我的代码:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    var window: NSWindow!

    override init() {
        super.init()
        print("Init")
    }

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        print("Finished launching")

        // Create a window
        window = NSWindow()

        // Add the view controller
        let viewController = ViewController()
        window.contentView?.addSubview(viewController.view)

        // Show the window
        window.makeKeyAndOrderFront(nil)
    }
}

init 或 applicationDidFinishLaunching(_ aNotification: Notification) 都没有被调用。任何帮助将不胜感激。

【问题讨论】:

  • 代码对我来说看起来非常好...没有理由不应该进入 init 或 applicationDidFinishLaunching...
  • 创建新项目时为什么不取消选择使用情节提要?
  • @LeoDabus 因为它仍然给了我一个 XIB 文件。奇怪的是,如果我将 XIB/Storyboard 文件删除为“主界面”但将其保留在项目中,AppDelegate 仍会被调用。但是,一旦我删除它,它就不会被调用。
  • 另一件事是很明显编译器正在找到该标志,因为当我删除该标志时,它说它找不到隐式入口/起点(这是有道理的)。

标签: swift xcode macos


【解决方案1】:

你需要在这里做一些事情

  1. 从 plist 中删除 NSMainStoryboardFile 键/值
  2. 创建一个NSApplication 子类并将其分配给Principal Class (NSPrincipalClass) 键。

名称必须完全符合您的模块名称。

  1. 在您的 NSApplication 子类中手动实例化您的委托并将其分配给 delegate 属性。

确保对委托对象保持强引用。我刚刚在这里使用了let。

class GrookApplication: NSApplication {

    let strongDelegate = AppDelegate()

    override init() {
        super.init()
        self.delegate = strongDelegate
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

例如一个简单的委托。

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    override init() {
        super.init()
        print("wassup")
        //conceptual proof of life init override
        //wait until applicationDidFinishLaunching , specially for UI
    }

    var window: NSWindow!
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        print("yo! I'm alive")
        window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 200, height: 200), styleMask: .titled, backing: .buffered, defer: false)
        window.makeKeyAndOrderFront(nil)
    }

}

EDIT 2018 经验证的 High Sierra

不尝试在init 内进行窗口或视图控制器初始化,这会导致双重应用初始化问题和崩溃。该应用程序在此阶段尚未完成启动。等到applicationDidFinishLaunching 触发任何重要操作。

【讨论】:

  • 如何保持强引用?我尝试了这个并且一切正常 - 显示了窗口,但是当我尝试将窗口的 styleMask 设置为 .titled 时,它给了我一个异常说 Assertion failure in -[X.XApplication init], /Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1504.82.104/AppKit.subproj/NSApplication.m:1778 2017-04-08 13:25:35.761585+0100 X[9073:1059806] [General] An uncaught exception was raised 2017-04-08 13:25:35.761601+0100 X[9073:1059806] [General] Creating more than one Application。
  • @TylerDurden 强引用是通过使用var 或let 完成的。如果您没有这样做,则弱委托引用会释放该实例。即这个self.delegate = AppDelegate() 不会工作。至于你的其他问题,我不知道你没有看到你的代码。我怀疑你试图这样做 [NSApplication sharedApplication].window 并且它没有工作的原因。
  • 请记住,此方法不适用于 OS X Yosemite :)
【解决方案2】:

Warren Burton 接受的答案,使用对 @NSApplicationMain-annotated AppDelegate 实例的强引用不再有效。我自己在 OS X High Sierra 上证实了这一点,Alex Sieroshtan 评论说它在 OS X Yosemite 中也不起作用。正如泰勒·德登(Tyler Durden)所指出的,失败点是这条信息:

Assertion failure in -[X.XApplication init], /Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1504.82.104/AppKit.subproj/NSApplication.m:1778
2017-04-08 13:25:35.761585+0100 X
[9073:1059806][General] An uncaught exception was raised 2017-04-08 13:25:35.761601+0100 X
[9073:1059806][General] Creating more than one Application

我自己为此苦苦挣扎了一段时间,但通过大量的实验提出了两个最新的解决方案。

选项 1:通过变通方法继续使用 @NSApplicationMain

我发现您可以更改已接受答案的代码以解决该错误。这样做的方法是不要在名为 AppDelegate 的类中调用 super.init() 方法。

什么?

真的。我认为AppDelegate(或沿着这些思路的一些逻辑)完成了一个过于急切的断言计数,因此对super.init()的调用以及override init()块的完成都会被计算在内。您有两种解决方法:

  1. 不要打电话给super.init():这实际上是可能的,completely healthy 用于NSObject,至少在 macOS 中是这样。但是,您将无法在 override init() 块中引用 self。

  2. 不要覆盖init():考虑在像applicationWillFinishLaunching(:) 这样的生命周期方法中执行初始化过程。

当然,我不推荐其中任何一个。

选项 2:完全放弃 @NSApplicationMain 方法

@NSApplicationMain 只是一个我们可以近似的宏。幸运的是,我遇到了 James H Fisher 的 blog post 解释方法。稍后我会引用重要的内容。

如果您在任何地方写过@NSApplicationMain,请在继续执行这些说明之前将其删除。

无需更改您的 Info.plist 文件

NSPrincipalClass 的键:值对应保持其默认值:

<key>NSPrincipalClass</key>
<string>NSApplication</string>

使用main.swift 而不是继承NSApplication

文件必须命名为main.swift;这是 Swift 的“顶级表达式不允许出现”规则的 special exception。

import AppKit

let app = NSApplication.shared
let delegate = AppDelegate()
app.delegate = delegate

_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

逻辑

James H Fisher 解释说,引用 NSApplication documentation:

[文档]

每个应用程序必须只有一个 NSApplication 实例(或 NSApplication 的子类)。你程序的main() 函数应该通过调用shared() 类方法来创建这个实例。

[詹姆斯]

首先,main.swift 运行NSApplication.shared,并将这个NSApplication 对象分配给myApp。请注意,文档引用了 main() 函数,即使在 Swift 中没有!等同于main.swift 文件。

接下来,main.swift 实例化您的AppDelegate 类,并将其分配为myApp 的.delegate。您现在可以看到为什么默认项目选择调用类AppDelegate:它被设置为.delegate 上的NSApplication。

最后,main.swift 调用函数NSApplicationMain(...)...函数NSApplicationMain(...) 是Cocoa 应用程序的入口点。 NSApplicationMain(...) 永不返回;相反,它会设置 UI 事件循环,并最终使用 C exit(...) 函数退出。

另外this StackOverflow post 详细说明了为什么使用sharedApplication 可以解决“创建多个应用程序”错误。

...这就是你所需要的!希望这对其他人有所帮助。

【讨论】:

【解决方案3】:

如果有人正在寻找 Swift 版本(基于 @WarrenBurtons 的回答)。

AppDelegate

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    var window: NSWindow?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        window = NSWindow(contentViewController: RootViewController())
        window?.makeKeyAndOrderFront(self)
    }
}

class RootViewController: NSViewController {
    override func loadView() {
      self.view = NSView()
      self.view.frame = NSRect(x: 0, y: 0, width: 600, height: 400)
  }
}

NSApplication 子类

import Cocoa

class Application: NSApplication {
    let strongDelegate = AppDelegate()

    override init() {
        super.init()
        self.delegate = strongDelegate
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Info.plist 条目

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    ...
    <key>NSPrincipalClass</key>
    <string>$(PRODUCT_MODULE_NAME).Application</string>
    ...
</dict>
</plist>

我还为此创建了一个要点,我将及时了解新的 Xcode / Swift 版本。 https://gist.github.com/florieger/7ac5e7155f6faf18666f92f7d82f6cbc

编辑:确保删除 Main.storyboard / MainMenu.xib,否则您可能会在 UI 调试器中出现两个窗口。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-21
    • 2015-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多