ios - programacion - tipos de funciones en swift




Uso de protocolos como tipos de matriz y parámetros de funciones en swift (4)

Quiero crear una clase que pueda almacenar objetos que se ajusten a un protocolo determinado. Los objetos deben almacenarse en una matriz de tipo. Según la documentación de Swift, los protocolos se pueden usar como tipos:

Como es un tipo, puede usar un protocolo en muchos lugares donde se permiten otros tipos, incluidos:

  • Como tipo de parámetro o tipo de retorno en una función, método o inicializador
  • Como el tipo de una constante, variable o propiedad
  • Como el tipo de elementos en una matriz, diccionario u otro contenedor

Sin embargo, lo siguiente genera errores de compilación:

El protocolo 'SomeProtocol' solo se puede usar como una restricción genérica porque tiene requisitos de tipo propio o asociado

¿Cómo se supone que resolver esto?

protocol SomeProtocol: Equatable {
    func bla()
}

class SomeClass {

    var protocols = [SomeProtocol]()

    func addElement(element: SomeProtocol) {
        self.protocols.append(element)
    }

    func removeElement(element: SomeProtocol) {
        if let index = find(self.protocols, element) {
            self.protocols.removeAtIndex(index)
        }
    }
}

Considero que su objetivo principal es mantener una colección de objetos que se ajusten a algún protocolo, agregar a esta colección y eliminarla de ella. Esta es la funcionalidad tal como se establece en su cliente, "SomeClass". La herencia equivalente se necesita y no es necesaria para esta funcionalidad. Podríamos haber hecho que esto funcione en matrices en Obj-C usando la función de "índice" que puede tomar un comparador personalizado, pero esto no es compatible con Swift. Entonces, la solución más simple es usar un diccionario en lugar de una matriz como se muestra en el siguiente código. He proporcionado getElements () que le devolverá la matriz de protocolos que deseaba. Entonces cualquiera que use SomeClass ni siquiera sabría que se usó un diccionario para la implementación.

Como en cualquier caso, necesitarías alguna propiedad distintiva para separar tus objetos, he asumido que es "nombre". Asegúrese de hacer su elemento element.name = "foo" cuando cree una nueva instancia de SomeProtocol. Si el nombre no está establecido, aún puede crear la instancia, pero no se agregará a la colección y addElement () devolverá "false".

protocol SomeProtocol {
    var name:String? {get set} // Since elements need to distinguished, 
    //we will assume it is by name in this example.
    func bla()
}

class SomeClass {

    //var protocols = [SomeProtocol]() //find is not supported in 2.0, indexOf if
     // There is an Obj-C function index, that find element using custom comparator such as the one below, not available in Swift
    /*
    static func compareProtocols(one:SomeProtocol, toTheOther:SomeProtocol)->Bool {
        if (one.name == nil) {return false}
        if(toTheOther.name == nil) {return false}
        if(one.name ==  toTheOther.name!) {return true}
        return false
    }
   */

    //The best choice here is to use dictionary
    var protocols = [String:SomeProtocol]()


    func addElement(element: SomeProtocol) -> Bool {
        //self.protocols.append(element)
        if let index = element.name {
            protocols[index] = element
            return true
        }
        return false
    }

    func removeElement(element: SomeProtocol) {
        //if let index = find(self.protocols, element) { // find not suported in Swift 2.0


        if let index = element.name {
            protocols.removeValueForKey(index)
        }
    }

    func getElements() -> [SomeProtocol] {
        return Array(protocols.values)
    }
}

Desea crear una clase genérica, con una restricción de tipo que requiera que las clases utilizadas con ella se ajusten a SomeProtocol , como esta:

class SomeClass<T: SomeProtocol> {
    typealias ElementType = T
    var protocols = [ElementType]()

    func addElement(element: ElementType) {
        self.protocols.append(element)
    }

    func removeElement(element: ElementType) {
        if let index = find(self.protocols, element) {
            self.protocols.removeAtIndex(index)
        }
    }
}

Ha accedido a una variante de un problema con los protocolos en Swift para los que aún no existe una buena solución.

Consulte también Ampliación de matriz para verificar si está ordenada en Swift. , contiene sugerencias sobre cómo solucionarlo que pueden ser adecuadas para su problema específico (su pregunta es muy genérica, quizás pueda encontrar una solución con estas respuestas).


La solución es bastante simple:

protocol SomeProtocol {
    func bla()
}

class SomeClass {
    init() {}

    var protocols = [SomeProtocol]()

    func addElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols.append(element)
    }

    func removeElement<T: SomeProtocol where T: Equatable>(element: T) {
        protocols = protocols.filter {
            if let e = $0 as? T where e == element {
                return false
            }
            return true
        }
    }
}




swift-protocols