Swift 2 - 符合Equatable问题的协议

时间:2015-10-23 02:54:38

标签: ios swift2

我在下面定义的协议存在问题。我有两个要求:

  1. 我希望能够将协议Peer用作其他类中的类型,同时保持具体类的私有性。
  2. 我想将协议存储在一个数组中,并能够确定实例的索引。
  3. 为了满足第二点,我需要使协议符合Equatable协议。但是当我这样做时,我不能再使用Peer作为类型,因为它需要被视为通用。这意味着我不能再具有私有的具体实现,并且要求1被打破。

    想知道是否有其他人遇到过这个问题并以某种方式解决了这个问题。也许我误解了我在indexOf ...

    时得到的错误

    Group.swift

    import Foundation
    
    class Group {
        var peers = [Peer]()
    
        init() {
            peers.append(PeerFactory.buildPeer("Buddy"))
        }
    
        func findPeer(peer: Peer) -> Bool {
            if let index = peers.indexOf(peer) {
                return true
            }
            return false
        }
    }
    

    Peer.swift

    import Foundation
    
    protocol Peer {
        var name: String { get }
    }
    
    class PeerFactory {
        static func buildPeer(name: String) -> Peer {
            return SimplePeer(name: name)
        }
    }
    
    private class SimplePeer: Peer {
        let name: String
    
        init(name: String) {
            self.name = name
        }
    }
    

    如果indexOf Peer,则Equatable出错:

    cannot convert value of type 'Peer' to expected argument type '@noescape (Peer) throws -> Bool'
    

2 个答案:

答案 0 :(得分:5)

所以我找到了一个解决方案,通过扩展Equatable来为CollectionType类型的元素定义一个新的indexOf来解决Peer要求,它利用了其他基于闭包的indexOf。这本质上是一个便利函数,它使我免于直接使用闭包indexOf。代码如下:

extension CollectionType where Generator.Element == Peer {
    func indexOf(element: Generator.Element) -> Index? {
        return indexOf({ $0.name == element.name })
    }
}

这当然假设我需要测试相等性的所有内容都可以从Peer协议获得(对于我的特定用例来说都是如此)。

编辑: Swift 3更新:

extension Collection where Iterator.Element == Peer {
    func indexOf(element: Iterator.Element) -> Index? {
        return index(where: { $0.name == element.name })
    }
}

答案 1 :(得分:1)

我建议您使用公共超类,因此该类可以符合Equatable

class Peer: Equatable {
    // Read-only computed property so you can override.
    // If no need to override, you can simply declare a stored property 
    var name: String {
        get {
            fatalError("Should not call Base")
        }
    }

    // should only be called from subclass
    private init() {}
}

private class SimplePeer: Peer {
    override var name: String {
        get {
            return _name
        }
    }

    let _name: String

    init(name: String) {
        _name = name
        super.init()
    }
}

func == (lhs: Peer, rhs: Peer) -> Bool {
    return lhs.name == rhs.name
}

class PeerFactory {
    static func buildPeer(name: String) -> Peer {
        return SimplePeer(name: name)
    }
}