2

Swift 3中,我希望能够创建一个协议,允许我添加元素并通过 using 进行迭代for element in。该协议应该同时适用于NSMutableSetNSMutableOrderedSet(因为它们不是从同一个类继承的)。

我知道有充分的理由为什么NSMutableSetNSMutableOrderedSet从同一个类继承,这里这里都有解释。

但我想创建一个协议,它只使用NSMutableSet(and NSMutableOrderedSet) 中所有方法的一小部分。

我已经add开始工作了,像这样:

protocol MutableSet {
    func add(_ element: Any)
}

extension NSMutableSet: MutableSet {}
extension NSMutableOrderedSet: MutableSet {}

let one: NSString = "one"
let two: NSString = "two"

// Works if created with `NSMutableSet`
let mutableSet: MutableSet = NSMutableSet()

mutableSet.add(one)
mutableSet.add(two)

for element in mutableSet as! NSMutableSet {
    print(element)
}
/*
 This prints:
 one
 two
*/

// Also works if creating `NSMutableOrderedSet` instance
let mutableOrderedSet: MutableSet = NSMutableOrderedSet()
mutableOrderedSet.add(one)
mutableOrderedSet.add(two)
for element in mutableOrderedSet as! NSMutableOrderedSet {
    print(element)
}
/*
 This prints:
 one
 two
 */

但是,我真的很想能够通过使用来遍历元素:

for element in mutableSet {
    print(element)
}

我试图使protocol MutableSet符合Sequence协议,像这样,但它不起作用:

protocol MutableSet: Sequence {
    func add(_ element: Any)
}

extension NSMutableSet: MutableSet {
    typealias Iterator = NSFastEnumerationIterator
    typealias Element = NSObject // I dont know what to write here
    typealias SubSequence = Slice<Set<NSObject>> // Neither here....
}

let one: NSString = "one"
let two: NSString = "two"

let mutableSet: MutableSet = NSMutableSet() // Compile Error: Protocol `MutableSet` can only be used as a generic constraint because it has Self or associated type requirements
mutableSet.add(one)
mutableSet.add(two)

for element in mutableSet { // Compile Error: Using `MutableSet` as a concrete type conforming to protocol `Sequence` is not supported
    print(element)
}

是否可以使我的协议符合Sequence?我该怎么做?我尝试了typealias和等associatedtype的各种组合。我也尝试了这个答案,它对我不起作用。ElementIterator

编辑 2:在编辑 1 中回答我自己的问题

我开始var count: Int { get }使用这个解决方案,但不确定它是否是最好的......如果不必在 and 的扩展中实现 也很好,var elements: [Any] { get }但我想这是不可避免的吗?NSMutableSetNSMutableOrderedSet

protocol MutableSet: Sequence {
    subscript(position: Int) -> Any { get }
    func add(_ element: Any)
    var count: Int { get }
    var elements: [Any] { get }
}

extension MutableSet {
    subscript(position: Int) -> Any {
        return elements[position]
    }
}

extension NSMutableSet: MutableSet {
    var elements: [Any] {
        return allObjects
    }
}
extension NSMutableOrderedSet: MutableSet {
    var elements: [Any] {
        return array
    }
}

struct AnyMutableSet<Element>: MutableSet {
    private let _add: (Any) -> ()
    private let _makeIterator: () -> AnyIterator<Element>

    private var _getElements: () -> [Any]
    private var _getCount: () -> Int

    func add(_ element: Any) { _add(element) }
    func makeIterator() -> AnyIterator<Element> { return _makeIterator() }

    var count: Int { return _getCount() }
    var elements: [Any] { return _getElements() }

    init<MS: MutableSet>(_ ms: MS) where MS.Iterator.Element == Element {
        _add = ms.add
        _makeIterator = { AnyIterator(ms.makeIterator()) }
        _getElements = { ms.elements }
        _getCount = { ms.count }
    }
}

let one: NSString = "one"
let two: NSString = "two"

let mutableSet: AnyMutableSet<Any>
let someCondition = true
if someCondition {
    mutableSet = AnyMutableSet(NSMutableSet())
} else {
    mutableSet = AnyMutableSet(NSMutableOrderedSet())
}
mutableSet.add(one)
mutableSet.add(two)

for i in 0..<mutableSet.count {
    print("Element[\(i)] == \(mutableSet[i])")
}

// Prints:
// Element[0] == one
// Element[1] == two

编辑1:跟进问题 使用@rob-napier 的优秀答案和type erasure技术我已经扩展了protocol MutableSet具有count和能力的能力,但是我只能使用丑陋的(named )subscript来做到这一点,而不是. 这就是我正在使用的:funcgetCountvar

protocol MutableSet: Sequence {
    subscript(position: Int) -> Any { get }
    func getCount() -> Int
    func add(_ element: Any)
    func getElements() -> [Any]
}

extension MutableSet {
    subscript(position: Int) -> Any {
        return getElements()[position]
    }
}

extension NSMutableSet: MutableSet {
    func getCount() -> Int {
        return count
    }

    func getElements() -> [Any] {
        return allObjects
    }
}
extension NSMutableOrderedSet: MutableSet {
    func getElements() -> [Any] {
        return array
    }

    func getCount() -> Int {
        return count
    }
}

struct AnyMutableSet<Element>: MutableSet {
    private var _getCount: () -> Int
    private var _getElements: () -> [Any]
    private let _add: (Any) -> ()
    private let _makeIterator: () -> AnyIterator<Element>

    func getElements() -> [Any] { return _getElements() }
    func add(_ element: Any) { _add(element) }
    func makeIterator() -> AnyIterator<Element> { return _makeIterator() }
    func getCount() -> Int { return _getCount() }

    init<MS: MutableSet>(_ ms: MS) where MS.Iterator.Element == Element {
        _add = ms.add
        _makeIterator = { AnyIterator(ms.makeIterator()) }
        _getElements = ms.getElements
        _getCount = ms.getCount
    }
}

let one: NSString = "one"
let two: NSString = "two"

let mutableSet: AnyMutableSet<Any>
let someCondition = true
if someCondition {
    mutableSet = AnyMutableSet(NSMutableSet())
} else {
    mutableSet = AnyMutableSet(NSMutableOrderedSet())
}
mutableSet.add(one)
mutableSet.add(two)

for i in 0..<mutableSet.getCount() {
    print("Element[\(i)] == \(mutableSet[i])")
}
// Prints:
// Element[0] == one
// Element[1] == two

我怎样才能让它在协议中使用,var count: Int { get }var elements: [Any]不是在函数中使用?

4

1 回答 1

3

几乎每一个“我如何使用 PAT(具有关联类型的协议)......”的答案都是“把它放在一个盒子里”。那个盒子是橡皮擦。在你的情况下,你想要一个AnyMutableSet.

import Foundation

// Start with your protocol
protocol MutableSet: Sequence {
    func add(_ element: Any)
}

// Now say that NSMutableSet is one. There is no step two here. Everything can be inferred.
extension NSMutableSet: MutableSet {}

// Create a type eraser for MutableSet. Note that I've gone ahead and made it generic.
// You could lock it down to just Any, but why limit yourself
struct AnyMutableSet<Element>: MutableSet {
    private let _add: (Any) -> ()
    func add(_ element: Any) { _add(element) }
    private let _makeIterator: () -> AnyIterator<Element>
    func makeIterator() -> AnyIterator<Element> { return _makeIterator() }
    init<MS: MutableSet>(_ ms: MS) where MS.Iterator.Element == Element {
        _add = ms.add
        _makeIterator = { AnyIterator(ms.makeIterator()) }
    }
}

// Now we can use it
let one: NSString = "one"
let two: NSString = "two"

// Wrap it in an AnyMutableSet
let mutableSet = AnyMutableSet(NSMutableSet())
mutableSet.add(one)
mutableSet.add(two)

for element in mutableSet {
    print(element)
}

原则上还有另一种方法,即直接使用现有的“协议,该协议允许我添加元素并通过使用 for element in 进行迭代”。这是两个协议:SetAlgebra & Sequence. 在实践中,我发现要么要么NSMutableSet符合NSOrderedSet要么SetAlgebra……令人讨厌。NSMutableSet在 Swift 3 中基本上被破坏了。它Any在各个地方都接受,但被定义为 over AnyHashable。基本代码不起作用:

let s = NSMutableSet()
let t = NSMutableSet()
s.union(t)

但那是因为你不应该使用NSMutableSet. 它会自动桥接到Set,您应该使用它Set。并且Set确实符合,SetAlgebra & Sequence这样就可以了。

但后来我们来了NSOrderedSet。这很难桥接到 Swift(这就是基金会团队推迟这么久的原因)。这真的是一种 IMO 类型的混乱,每次我尝试使用它时,我都会把它拔出来,因为它不能很好地与任何东西配合使用。(尝试使用 NSFetchedResultsController 在“有序关系”中使用顺序。)坦率地说,您最好的选择是将其包装在一个结构中并使该结构符合SetAlgebra & Sequence.

但是如果你不这样做(或者只是摆脱有序集合,就像我最终所做的那样),那么类型擦除几乎是你唯一的工具。

于 2016-09-20T15:05:47.043 回答