objective-c在使用之前,每个类都在每个类中声明一个类函数,initialize((。它通常被用作交换方法实现(Swizzling(的切入点。它的使用在Swift 3.1中被弃用。
这是我过去做的:
extension NSView {
public override class func initialize() {
// This is called on class init and before `applicationDidFinishLaunching`
}
}
如果没有initialize
?
我需要它作为一个框架,因此需要在AppDelegate中调用某些东西是不做的。我需要在applicationDidFinishLaunching
。
我真的很喜欢这个解决方案。这正是我要寻找的,但这是针对iOS的。我需要Macos。有人可以建议这样做吗?
要具体,我需要等同于MacOS:
extension UIApplication {
private static let runOnce: Void = {
// This is called before `applicationDidFinishLaunching`
}()
override open var next: UIResponder? {
UIApplication.runOnce
return super.next
}
}
我尝试过覆盖NSApplication
上的各种属性,但没有成功。
解决方案需要在纯Swift中。没有Objective-C。
编辑:自从我写了此答案以来,OP已在编辑中添加了"纯Swift"。但是,我在这里留下了这个答案,因为在撰写本文时,这仍然是唯一正确的方法。希望模块初始化挂钩将在Swift 6或7或8中添加,但是截至2018年3月,Pure Swift是此用例的错误工具。
原始答案如下:
不幸的是,Swift与旧的initialize()
和load()
方法没有任何直接等效的,因此在Pure Swift Afaik中无法完成。但是,如果您不愿意将少量的Objective-C混合到您的项目中,那么这并不难。只需完成一个完全暴露于Objective-C的迅速课程:
class MyInitThingy: NSObject {
@objc static func appWillLaunch(_: Notification) {
print("App Will Launch")
}
}
然后将此简短的Objective-C文件添加到项目:
#import <Cocoa/Cocoa.h>
static void __attribute__ ((constructor)) Initer() {
// Replace "MyFrameworkName" with your framework's module name.
// You can also just #import <MyFrameworkName/MyFrameworkName-Swift.h>
// and then access the class directly, but that requires the class to
// be public, which pollutes the framework's external interface.
// If we just look up the class and selector via the Objective-C runtime,
// we can keep everything internal.
Class class = NSClassFromString(@"MyFrameworkName.MyInitThingy");
SEL selector = NSSelectorFromString(@"appWillLaunch:");
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:class
selector:selector
name:NSApplicationWillFinishLaunchingNotification
object:nil];
}
使用这两个代码,链接到您的框架的应用程序应在applicationDidFinishLaunching
被调用之前登录到控制台的应用程序。
另外,如果您的模块中已经有一个公共OBJC可见类,则可以通过类别来执行此操作,而无需使用运行时函数:
public class SomeClass: NSObject {
@objc static func appWillLaunch(_: Notification) {
print("App Will Launch")
}
}
和:
#import <Cocoa/Cocoa.h>
#import <MyFrameworkName/MyFrameworkName-Swift.h>
@interface SomeClass (InternalSwiftMethods)
+ (void)appWillLaunch:(NSNotification *)notification;
@end
@implementation SomeClass (FrameworkInitialization)
+ (void)load {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(appWillLaunch:)
name:NSApplicationWillFinishLaunchingNotification
object:nil];
}
@end
nope,不存在initialize()
的Swift替代品,主要是因为Swift在静态上派遣了Swift,因此无法拦截方法调用。而且实际上不再需要,因为该方法通常用于初始化来自Objective-C文件的静态变量,并且在Swift静态/全局变量中总是懒惰的,可以在表达式的结果下初始化(目标 - c(。
即使可以实现类似的东西,我也会阻止您在不了解框架用户的情况下隐式运行东西。我建议在库中的某个地方添加configure
方法,并要求库的用户调用它。这样,他们可以控制初始化点。只有我链接的框架(但不再(或尚未((未经我的同意就开始执行代码。
让框架用户在想要框架代码启动时控制框架用户是卫生的。如果您的代码确实必须在应用程序生命周期的开始时运行,请要求框架用户在其他任何调用之前拨打框架入口点。正确配置您的框架也是他们的兴趣。
例如:
MyFramework.configure(with: ...)
// or
MyManager.start()
正如其他人先前指出的那样,在Swift中的框架中做您要求的事情既不可能(也不是好的编程(。从应用程序本身(此类行为所属的地方(实现功能非常简单 - 无需弄乱通知或选择器。您只需覆盖NSApplicationDelegate
(或UIApplicationDelegate
(的init
,然后在此设置类的初始化器:
class AppDelegate: NSObject, NSApplicationDelegate {
override init() {
super.init()
YourClass.initializeClass()
}
}
和相应的静态函数:
class YourClass {
static func initializeClass() {
// do stuff
}
}
这将达到与initialize()
相同的功能。