简体   繁体   English

现在Swift已经弃用它,是否有替代macOS中的initialize()?

[英]Is there an alternative to initialize() in macOS now that Swift has deprecated it?

Objective-C declares a class function, initialize(), that is run once for each class, before it is used. Objective-C声明了一个类函数initialize(),它在每个类使用之前运行一次。 It is often used as an entry point for exchanging method implementations (swizzling), among other things. 它经常被用作交换方法实现(混合)等的入口点。 Its use was deprecated in Swift 3.1. 它的使用在Swift 3.1中被弃用。

This is what I used to do: 这就是我以前做的事情:

extension NSView {
    public override class func initialize() {
        // This is called on class init and before `applicationDidFinishLaunching`
    }
}

How can I achieve the same thing without initialize ? 如果没有initialize我怎样才能实现同样的目标?

I need it for a framework, so requiring calling something in the AppDelegate is a no-go. 我需要它用于框架,所以要求在AppDelegate中调用一些东西是不行的。 I need it called before applicationDidFinishLaunching . 我需要在applicationDidFinishLaunching之前调用它。

I really like this solution. 我非常喜欢这个解决方案。 It's exactly what I'm looking for, but it's for iOS. 这正是我正在寻找的,但它适用于iOS。 I need it for macOS. 我需要它用于macOS。 Could someone suggest a macOS version of that? 有人可以推荐一个macOS版本吗?

To be specific, I need the equivalent of this, but for macOS: 具体来说,我需要相当于此,但对于macOS:

extension UIApplication {
    private static let runOnce: Void = {
        // This is called before `applicationDidFinishLaunching`
    }()

    override open var next: UIResponder? {
        UIApplication.runOnce
        return super.next
    }
}

I've tried overriding various properties on NSApplication with no success. 我已经尝试在NSApplication上覆盖各种属性但没有成功。

The solution needs to be in pure Swift. 解决方案需要纯粹的Swift。 No Objective-C. 没有Objective-C。

Nope, a Swift alternative to initialize() doesn't exist, mainly because Swift is statically dispatched, so method calls can't be intercepted. 不,swift替代initialize()不存在,主要是因为Swift是静态调度的,所以方法调用不能被截获。 And is really no longer needed, as the method was commonly used to initialize static variables from Objective-C files, and in Swift static/global variables are always lazy and can be initialized with the result of an expression (thing not possible in Objective-C). 并且实际上不再需要,因为该方法通常用于初始化Objective-C文件中的静态变量,并且在Swift中静态/全局变量总是很懒并且可以使用表达式的结果进行初始化(在Objective-中不可能的事情) C)。

Even if it would be possible to achieve something similar, I would discourage you to implicitly run stuff without the knowledge of the framework users. 即使有可能实现类似的东西,我也不鼓励你在没有框架用户知识的情况下隐式运行东西。 I'd recommend to add a configure method somewhere in the library and ask the users of your library to call it. 我建议在库中的某处添加一个configure方法,并要求库的用户调用它。 This way they have control over the initialization point. 这样他们就可以控制初始化点。 There are only a few things worser than having a framework that I linked against, but no longer (or not yet) using, to start executing code without my consent. 只有少数事情比我链接的框架更糟糕,但不再(或尚未)使用,未经我的同意开始执行代码。

It's just saner to give framework users control over when they want the framework code to start. 让框架用户可以控制他们何时希望框架代码启动,这是更合理的。 If indeed your code must run at the very beginning of the application lifecycle, then ask the framework users to make the call to your framework entry point before any other calls. 如果您的代码确实必须在应用程序生命周期的最开始运行,那么请求框架用户在任何其他调用之前调用您的框架入口点。 It's also in their interest for your framework to be properly configured. 正确配置框架也符合他们的利益。

Eg: 例如:

MyFramework.configure(with: ...)
// or
MyManager.start()

EDIT: Since I wrote this answer, the OP has added "pure Swift" to the question in an edit. 编辑:由于我写了这个答案,OP在编辑中添加了“纯Swift”问题。 However, I am leaving this answer here because it remains the only correct way to do this at the time of this writing. 但是,我在这里留下这个答案,因为在撰写本文时,它仍然是唯一正确的方法。 Hopefully, module initialization hooks will be added in Swift 6 or 7 or 8, but as of March 2018, pure Swift is the wrong tool for this use case. 希望模块初始化挂钩将添加到Swift 6或7或8中,但是截至2018年3月,纯Swift是这个用例的错误工具

Original answer follows: 原始答案如下:

Unfortunately, Swift doesn't have any direct equivalent to the old initialize() and load() methods, so this can't be done in pure Swift AFAIK. 不幸的是,Swift与旧的initialize()load()方法没有任何直接的等价,所以这不能在纯Swift AFAIK中完成。 However, if you're not averse to mixing a small amount of Objective-C into your project, this isn't hard to do. 但是,如果您不反对将少量Objective-C混合到您的项目中,那么这并不难。 Simply make a Swift class that's fully exposed to Objective-C: 只需创建一个完全暴露于Objective-C的Swift类:

class MyInitThingy: NSObject {
    @objc static func appWillLaunch(_: Notification) {
        print("App Will Launch")
    }
}

Then add this short Objective-C file to the project: 然后将这个简短的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];
}

With these two pieces of code in place, an app that links against your framework should get "App Will Launch" logged to the console sometime before applicationDidFinishLaunching is called. 有了这两段代码,一个链接到你的框架的应用程序应该在调用applicationDidFinishLaunching之前的某个时间将“App Will Launch”记录到控制台。

Alternatively, if you already have a public ObjC-visible class in your module, you can do this without having to use the runtime functions, via a category: 或者,如果您的模块中已经有一个公共ObjC可见类,则可以通过以下类别执行此操作而无需使用运行时功能:

public class SomeClass: NSObject {
    @objc static func appWillLaunch(_: Notification) {
        print("App Will Launch")
    }
}

and: 和:

#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

As pointed out previously by others it is neither possible (nor good programming) to do what you ask for in a Framework in Swift. 正如其他人之前所指出的那样,在Swift框架中执行你所要求的工作既不可能(也不是很好的编程)。 Achieving the functionality from the application itself (where this sort of behaviour belongs) is fairly simple though - no need to mess with notifications or selectors. 从应用程序本身(这种行为所属)实现功能非常简单 - 无需混淆通知或选择器。 You simply override the init of your NSApplicationDelegate (or UIApplicationDelegate ) and set up your class initializer there: 您只需覆盖NSApplicationDelegate (或UIApplicationDelegate )的init并在那里设置类初始值设定项:

class AppDelegate: NSObject, NSApplicationDelegate {

    override init() {
        super.init()
        YourClass.initializeClass()
    }
}

And the corresponding static function: 和相应的静态函数:

class YourClass {
    static func initializeClass() {
        // do stuff
    }
}

This will achieve the same functionality as initialize() . 这将实现与initialize()相同的功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 animation(forKey:) 的替代方案(现已弃用)? - Alternative to animation(forKey:) (now deprecated)? 既然 launchUserNotificationUserInfoKey 已被弃用,我如何检测应用程序是否是通过用户在 macOS 上单击通知来启动的? - How can I detect if app was launched by user clicking notification on macOS now that launchUserNotificationUserInfoKey has been deprecated? 在Swift 3.1中,不推荐使用UnsafeMutablePointer.initialize(from :) - In Swift 3.1, UnsafeMutablePointer.initialize(from:) is deprecated 如何在Swift 4的UIViewController中转换viewDidLoad方法? swift 4不推荐使用initialize() - How to swizzle viewDidLoad method in UIViewController in swift 4? initialize() is deprecated in swift 4 现在已弃用什么代替init(name:float :) - what replaces init(name:float:) now that it has been deprecated 不推荐使用MPMoviePlayerController,现在怎么办? - MPMoviePlayerController deprecated, what now? Swift,让我着迷于此:iOS 8.0中不推荐使用interfaceOrientation - Swift, has got me on this one: interfaceOrientation was deprecated in iOS 8.0 Spotify API弃用的功能无可替代 - Spotify API deprecated functions no alternative 替代 iOS 已弃用的 UIApplicationExitsOnSuspend - Alternative to iOS's deprecated UIApplicationExitsOnSuspend CTCallCenter已弃用。 有什么选择? - CTCallCenter is deprecated. What is the alternative?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM