在swift中使用协议作为数组类型和函数参数

sno*_*nod 130 generics ios swift swift-protocols

我想创建一个可以存储符合特定协议的对象的类.对象应存储在类型化数组中.根据Swift文档协议可以用作类型: 

因为它是一种类型,所以您可以在允许其他类型的许多地方使用协议,包括:

  • 作为函数,方法或初始值设定项中的参数类型或返回类型
  • 作为常量,变量或属性的类型
  • 作为数组,字典或其他容器中的项类型

但是,以下生成编译器错误:

协议'SomeProtocol'只能用作通用约束,因为它具有Self或相关类型要求

你怎么解决这个问题:

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Dar*_*ust 46

你已经遇到了Swift协议问题的一个变种,但尚未找到好的解决方案.

另请参阅扩展数组以检查它是否在Swift中排序?,它包含有关如何解决它的建议,可能适合您的特定问题(您的问题非常通用,也许您可​​以找到使用这些答案的解决方法).


Nat*_*ook 32

您想要创建一个泛型类,其类型约束要求与其一起使用的类符合SomeProtocol,如下所示:

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


wer*_*ver 13

在Swift中有一类特殊的协议,它不提供实现它的类型的多态性.这些协议在其定义中使用Self或使用associatedtype关键字(并且Equatable是其中之一).

在某些情况下,可以使用类型擦除的包装器使您的集合同态.以下是一个例子.

// This protocol doesn't provide polymorphism over the types which implement it.
protocol X: Equatable {
    var x: Int { get }
}

// We can't use such protocols as types, only as generic-constraints.
func ==<T: X>(a: T, b: T) -> Bool {
    return a.x == b.x
}

// A type-erased wrapper can help overcome this limitation in some cases.
struct AnyX {
    private let _x: () -> Int
    var x: Int { return _x() }

    init<T: X>(_ some: T) {
        _x = { some.x }
    }
}

// Usage Example

struct XY: X {
    var x: Int
    var y: Int
}

struct XZ: X {
    var x: Int
    var z: Int
}

let xy = XY(x: 1, y: 2)
let xz = XZ(x: 3, z: 4)

//let xs = [xy, xz] // error
let xs = [AnyX(xy), AnyX(xz)]
xs.forEach { print($0.x) } // 1 3
Run Code Online (Sandbox Code Playgroud)


alm*_*mas 12

我找到的有限解决方案是将协议标记为仅类协议.这将允许您使用'==='运算符比较对象.我知道这对结构等不起作用,但在我的情况下这已经足够了.

protocol SomeProtocol: class {
    func bla()
}

class SomeClass {

    var protocols = [SomeProtocol]()

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

    func removeElement(element: SomeProtocol) {
        for i in 0...protocols.count {
            if protocols[i] === element {
                protocols.removeAtIndex(i)
                return
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)


bzz*_*bzz 7

解决方案非常简单:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你错过了重要的事情:OP希望协议继承`Equatable`协议.它有很大的不同. (4认同)

ste*_*tef 1

Swift 5.7 / Xcode 14开始,现在可以使用any.

protocol SomeProtocol: Equatable {
    func bla()
}

class SomeClass {
    var protocols = [any SomeProtocol]()
    
    func addElement(element: any SomeProtocol) {
        protocols.append(element)
    }
    
    func removeElement(element: any SomeProtocol) {
        if let index = find(protocols, element) {
            protocols.remove(at: index)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)