简体   繁体   中英

Swift 2 - Protocol conforming to Equatable issue

I have an issue with a protocol I've defined below. I've got two requirements:

  1. I'd like to be able to use the protocol Peer as a type in other classes while keeping the concrete class private.
  2. I'd like to store the protocol in an array and be able to determine the index of an instance.

In order to satisfy the second point, I need to make the protocol conform to the Equatable protocol. But when I do that, I can no longer use Peer as a type, since it needs to be treated as a generic. This means I cannot have the concrete implementation private anymore, and requirement 1 is broken.

Wondering if anyone else has encountered this problem and gotten around it somehow. Maybe I'm misinterpreting the error I'm getting at 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
    }
}

Error at indexOf if Peer is not Equatable :

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

So I found a solution to get around the Equatable requirement by extending CollectionType to define a new indexOf for elements are of Peer type, which takes advantage of the other closure-based indexOf . This is essentially a convenience function which saves me from using the closure indexOf directly. Code below:

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

This of course assumes everything I need to test equality can be obtained from the Peer protocol (which is true for my specific use case).

EDIT: Update for Swift 3:

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

I would suggest you use public super class, so the class can conform to 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)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM