Class NSApplication
- 管理应用程序的主要事件循环和所有应用程序对象使用的资源对象;
概述:
每个应用程序使用一个 NSApplication 实例来控制主事件循环,跟踪应用程序的窗口和菜单,将事件分发到适当的对象(即本身或者其中的一个窗口),设置自动释放池,并接受应用级事件的通知. NSApplication 对象有一个委托(程序员分配的对象),当应用程序启动或终止,被隐藏或者激活时,应该打开用户选择的文件,等等.通过设置代理(delegate)和实现代理方法,程序员可以自定义程序的行为,而无需再次子类话 NSApplication. 在应用程序的 main() 函数中,通过调用 NSApplication 的 sharedApplication 方法来创建 NSApplication 的实例.创建应用程序对象后,在 main() 函数中应该加载应用程序的主 nib 文件,然后通过发应用程序对象的 run 信息来启动循环事件.如果在 Xcode 中创建一个应用程序项目, 那么这个 main() 函数是已经为程序员创建好的. main () 函数Xcode 的创建调用名为 NSApplicationMain() 的函数, 其功能类似于一下内容:
void NSAppilcationMain (int argc, char *argv[]) {
[NSApplication sharedApplication];
[NSBundle loadNibNamed:@"myMain" owner NSApp];
[NSApp run];
}
sharedApplication 类方法初始化显示环境, 并将应用程序连接到窗口服务器和显示服务器. NSApplication 对象维护 app 使用列表中的所有NSWindow对象. 因此它可以检索任何应用程序的NSView对象. sharedApplication 方法还初始化全局变量的NSApp.程序员可以使用它来检索NSApplication实例. sharedApplication 只执行一次初始化;如果你多次调用它,它只会返回之前所创建的对象.
共享的 NSApplication 对象执行从窗口服务器接收事件并将其分发到正确的重要任务 NSResponder 类. NSApp 将事件翻译成 NSEvent 对象, 然后将事件对象转发到受影响的NSWindow对象.所有的键盘和鼠标事件都直接转到与事件关联的NSWindow对象. 该规则的唯一例外是如果法伤按键事件时按下Command键; 在这种情况下,每个 NSWindow 对象都有机会响应事件. 当窗口对象从 NSApp 接收到 NSEvent 对象时, 它将其分发到对应视图层次结构中的对象.
NSApplication 也负责派发应用程序收到的某些 Apple 事件. 例如, macOS 会再不同时间将 Apple 事件发送到应用程序, 例如应用程序启动或者重新打开时. NSApplication 通过向适当的对象发送消息来安装 Apple 事件处理程序(Apple event handlers)来处理这些事件. 也可以使用 NSAppleEventManager 类来注册自己的 Apple 事件操作者 (Apple event handlers). 通常最好在
applicationWillFinishLaunching: 方法中去做这些事情.
NSApplication 类在初始化期间和事件循环内设置@autorelease block , 特别是在其初始化期间 (或者 sharedApplication)和 run 方法. 类似的, AppKit 添加 NSBundle 的方法在加载 nib 文件期间使用@autorelease blocks. 在这些@ autorelease blocks 块在相应的范围之外是不可访问 NSApplication 和 NSBundle 的方法的. 通常, app 在事件循环正在运行或通过从 nib 文件中加载对象时创建对象, 因此缺乏访问(不能访问)基本不是问题. 但是,如果确实需要在 main() 函数中使用 cocoa 类(加载 nib 文件或者实例化 NSApplication 之外),则应该创建一个@autorelease blocks 以包含使用该类代码.
代理和通知
你可以为 NSApplication 对象指定 delegate, 这个 delegate 代表对象回应某些特定的消息. 其中的一些信息例如: application(:openFile:),请求 delegate 执行一个动作. 另一个消息, applicationShouldTerminate(:),让 delegate 确认允许应用程序退出. NSApplication 类将这些消息直接发送给 delegate.
NSApplication 还会将通知发送到应用的默认通知中心. 任何对象可以通过 addObserver (:selector:name:object:) 方法注册一个监听事件到默认通知中心(NSNotifiactionCenter 类的一个实例),来接收 NSApplication 发布的一个或者多个通知. 如果 NSApplication 的的 delegate 实现了某些委托方法,则会自动注册 NSApplication 的代理接收这些通知.例如: NSApplication 将在即将完成启动应用程序以及启动应用程序时发布通知(willFinishLaunchingNotification 和 didFinishLaunchingNotification). delegate 有机会响应这些通知,通过实施这些方法: applicationWillFinishLaunching(:) 和 applicationDidFinishLaunching(:). 如果 delegate 想要通知这两个事件, 它必须实现这两种方法. 如果只需要知道应用程序的启动完成,那么只需要实现 applicationDidFinishLaunching(:)的方法可以了.
系统服务
NSApplication 于系统服务架构进行交互, 通过"服务"菜单为应用程序提供服务.
子类注释(NSApplication)
你会发现,很少需要去创建一个自定义的 NSApplication 的子类.与某些面向对象的库不同, Cocoa 不要求将 NSApplication 子类化以自定义应用程序的行为. 相反,它为程序员提供了许多其他方式来自定义应用程序.本节讨论 NSApplication 子类化的一些可能原因, 以及不将 NSApplication 进行子类化的一些原因.
要使用 NSApplication 自定义子类,只要发送共享到你的子类而不是直接到 NSApplication. 如果在 Xcode 中创建应用程序, 可以通过将自定义应用程序类设置为主类来实现. 在 Xcode 中,双击"Groups and Files "列表中的应用程序目标, 以打开目标的 "Info" 窗口. 然后显示窗口的 "Properties" 窗格,并使用自定义类的名称在 "Principal Class " 中替换 "NSApplication" . NSApplicationMain 函数发送共享到主类获取全局应用程序实例(NSApp) - 在这种情况下, 这将是程序员自定义 NSApplication 子类的一个实例.
重要:
许多 AppKit 类依赖于 NSApplication 类, 并且在该类完全初始化之前可能无法正常工作. 因此,不应该尝试从 NSApplication 子类的初始化方法里调用其他 AppKit 类的方法.
覆盖方法
通常, 程序员习惯性的在向全局应用对象(NSApp)发送消息时, 提供或者实现自有的特殊方法. 这时,可以在 NSApplication 子类化里覆盖原始方法来实现. 以下是候选的4中覆盖方法:
- 覆盖(Override) run() ---- 如果你希望应用程序不是以默认的方式启动管理主事件的循环.(这是一个关键和复杂的任务, 但是, 有理由尝试).
- 覆盖(Override) sendEvent(_:) ---- 如果要更改事件的调度方式或者执行一些特殊的事件处理.
- 覆盖(Override) requestUserAttention(_:) ---- 如果想修改应用程序如何吸引用户注意力(例如,提供一个代替的弹跳应用程序图标在 Dock).
- 覆盖(Override) target(forAction:) ---- 为 target 的动作消息替换另一个对象.
特别注意事项
全局应用对象在其使用@autorelease blocks 在它的 run()方法; 如果你需要覆盖(Override)此方法,则血药创建自己的@autorelease blocks. 不要覆盖sharedApplication 方法.app 默认的必要行为太过于复杂以至于无法在程序员自定义的方法中复制.
子类化的替代方法
NSApplication 定义了许多 delegate 方法, 这些 delegate 方法提供了修改应用程序某些特定行为的机会. app delegate 能够执行1个或者多个这些方法以达成你的设计目的. 一般来说, 比子类化更好的设计 NSApplication 是将应用程序的特殊行为表达的代码放入一个或者多个自定义对象的控制器中. 控制器中定义的方法可以从一个小的调度器对象调用, 而不会与全局应用程序对象紧密相连.