现在,Swift弃用了它,是否有替代品可以替代MacOS



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()相同的功能。

相关内容

  • 没有找到相关文章

最新更新