tutorial programación objective lenguaje caracteristicas objective-c swift interpolation

programación - Extendiendo una clase Swift con la categoría Objective C



objective-c++ (3)

Estoy en una situación en la que necesito usar la categoría de Objective C para extender una clase Swift. He hecho algo como sigue:

En "SomeClass.swift":

class SomeClass: NSObject { }

En "SomeClass + Extension.h":

#import "Project-Swift.h" @interface SomeClass (Extension) -(void)someMethod(); @end

Esto ha funcionado bien. Y si trato de usar la extensión SomeClass en mi código de Objective C, está bien.

El problema es que si quiero usar someMethod() en otra clase Swift, necesitaré colocar el archivo SomeClass+Extension.h en mi archivo ObjC-BridgingHeader.h .

Pero hacer esto causará una dependencia circular, porque SomeClass+Extension.h también importa Project-Swift.h .

¿Alguien tiene una buena manera de evitar esto?

Tenga en cuenta que simplemente la declaración de reenvío de la clase en el encabezado de la categoría no funcionará, ya que las categorías no pueden usar declaraciones de reenvío para su propia implementación, por lo que:

@class SomeClass sin importar Project-Swift.h dará un error de compilación.


El malo

Yo también he estado luchando este tema un montón. desafortunadamente, la documentation indica bastante explícitamente que este patrón no está permitido:

Para evitar referencias cíclicas, no importe código Swift en un archivo de encabezado (.h) de Objective-C. En su lugar, puede reenviar declarar una clase o protocolo Swift para hacer referencia a él en una interfaz Objective-C.

Las declaraciones de reenvío de clases y protocolos de Swift solo pueden usarse como tipos para declaraciones de métodos y propiedades.

también a lo largo de la página enlazada notará que sigue mencionando la importación del encabezado generado específicamente en el archivo .m:

Para importar código Swift en Objective-C desde el mismo destino

Importe el código Swift de ese objetivo a cualquier archivo .m de Objective-C dentro de ese objetivo

El bueno

Una solución que puede funcionar para usted es crear una extensión rápida que redefine cada método que necesite en la categoría. Es frágil y fea, pero podría decirse que es la solución más limpia.

/** Add category methods from objc here (since circular references prohibit the ObjC extension file) */ extension SomeClass { @nonobjc func someMethod() { self.performSelector(Selector("someMethod")) } }

  • agregar el @noobjc al frente permite usar la misma firma de método sin anular la implementación de ObjC
  • Ahora se puede eliminar la import "SomeClass+Extension.h" del encabezado de puente

si se necesita soporte para más de dos parámetros de entrada, o si se desea un tipo de acoplamiento más estrecho, recomendaría usar el tiempo de ejecución para llamar a la función subyacente. Una gran descripción está here .


Como una solución simple, puede mover la extensión a su código Swift. Entonces no tendrás ningún problema de dependencia.


Desde la guía de Interoperabilidad , no podemos acceder directamente a los objetos Objc subclasificados / categorizados / ampliados para la clase .swift [SomeClass].

Pero como un cambio, podemos hacer esto:

Para Variables, podemos hacer esto:

extension Class { private struct AssociatedKeys { static var DescriptiveName = "sh_DescriptiveName" } var descriptiveName: String? { get { return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String } set { if let newValue = newValue { objc_setAssociatedObject( self, &AssociatedKeys.DescriptiveName, newValue as NSString?, .OBJC_ASSOCIATION_RETAIN_NONATOMIC ) } } } }

Para los métodos, podemos usar method_swizzling que no se recomienda.