the programming para org language español descargar apple swift macos swizzling

programming - ¿Existe una alternativa para inicializar() en macOS ahora que Swift la ha desaprobado?



the swift programming language pdf (3)

Objective-C declara una función de clase, initialize (), que se ejecuta una vez para cada clase, antes de que se use. A menudo se usa como punto de entrada para intercambiar implementaciones de métodos (swizzling), entre otras cosas. Su uso quedó en desuso en Swift 3.1.

Esto es lo que solía hacer:

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

¿Cómo puedo lograr lo mismo sin initialize ?

Lo necesito para un marco, por lo que no es necesario llamar a algo en el AppDelegate. Lo necesito llamado antes de applicationDidFinishLaunching .

Realmente me gusta this solución. Es exactamente lo que estoy buscando, pero es para iOS. Lo necesito para macOS. ¿Podría alguien sugerir una versión macOS de eso?

Para ser específico, necesito el equivalente de esto, pero para macOS:

extension UIApplication { private static let runOnce: Void = { // This is called before `applicationDidFinishLaunching` }() override open var next: UIResponder? { UIApplication.runOnce return super.next } }

He intentado anular varias propiedades en NSApplication sin éxito.

La solución necesita estar en Swift puro. No Objective-C.


Como lo señalaron otros, no es posible (ni es una buena programación) hacer lo que usted pide en un Framework en Swift. Sin embargo, lograr la funcionalidad de la aplicación en sí (a la que pertenece este tipo de comportamiento) es bastante simple, sin necesidad de meterse con las notificaciones o los selectores. Simplemente anula el init de tu NSApplicationDelegate (o UIApplicationDelegate ) y configuras tu inicializador de clase allí:

class AppDelegate: NSObject, NSApplicationDelegate { override init() { super.init() YourClass.initializeClass() } }

Y la correspondiente función estática:

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

Esto logrará la misma funcionalidad que initialize() .


No, no existe una alternativa Swift a initialize() , principalmente porque Swift se envía de forma estática, por lo que no se pueden interceptar las llamadas de método. Y realmente ya no es necesario, ya que el método se usó comúnmente para inicializar variables estáticas desde archivos de Objective-C, y en Swift las variables estáticas / globales son siempre perezosas y se pueden inicializar con el resultado de una expresión (cosa que no es posible en Objective- DO).

Incluso si fuera posible lograr algo similar, le desalentaría que ejecutara cosas implícitamente sin el conocimiento de los usuarios del marco. Recomiendo agregar un método de configure en algún lugar de la biblioteca y pedir a los usuarios de su biblioteca que lo llamen. De esta manera tienen control sobre el punto de inicialización. Solo hay unas pocas cosas que son peores que tener un marco que vinculé, pero que ya no utilizo (o todavía no) para comenzar a ejecutar código sin mi consentimiento.

Es lógico darles a los usuarios del marco el control sobre cuándo quieren que comience el código del marco. Si de hecho su código debe ejecutarse al comienzo del ciclo de vida de la aplicación, pida a los usuarios del marco que realicen la llamada a su punto de entrada del marco antes que cualquier otra llamada. También les interesa que su marco esté configurado correctamente.

P.ej:

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


EDITAR: Desde que escribí esta respuesta, el OP ha agregado "Swift puro" a la pregunta en una edición. Sin embargo, estoy dejando esta respuesta aquí porque sigue siendo la única forma correcta de hacerlo al momento de escribir este artículo. Con suerte, los ganchos de inicialización del módulo se agregarán en Swift 6 o 7 u 8, pero a partir de marzo de 2018, Swift puro es la herramienta incorrecta para este caso de uso.

La respuesta original sigue:

Desafortunadamente, Swift no tiene ningún equivalente directo a los antiguos métodos initialize() y load() , por lo que esto no se puede hacer en Swift AFAIK puro. Sin embargo, si no es reacio a mezclar una pequeña cantidad de Objective-C en su proyecto, no es difícil hacerlo. Simplemente haga una clase Swift que esté completamente expuesta a Objective-C:

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

Luego agregue este archivo corto de Objective-C al proyecto:

#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]; }

Con estas dos piezas de código en su lugar, una aplicación que se vincule con su marco de trabajo debe hacer que "la aplicación se inicie" se registre en la consola en algún momento antes de applicationDidFinishLaunching se llame a applicationDidFinishLaunching .

Alternativamente, si ya tiene una clase pública ObjC-visible en su módulo, puede hacerlo sin tener que usar las funciones de tiempo de ejecución, a través de una categoría:

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

y:

#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