协议将NSMutableSet和NSMutableOrderedSet桥接在一起
在Swift 3中 ,我希望能够创建一个协议,允许我添加元素并迭代使用for element in
。 该协议应该适用于NSMutableSet
和NSMutableOrderedSet
(因为它们不从同一个类inheritance)。
我知道NSMutableSet
和NSMutableOrderedSet
没有从同一个类inheritance的原因很充分, 这里和这里都有解释。
但我想创建一个协议,它只使用NSMutableSet
(和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> // 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
的Element
, Iterator
等。我也试过这个答案它对我不起作用。
编辑2:在编辑1中回答我自己的问题
我得到了var count: Int { get }
使用这个解决方案工作,不确定它是否是最好的…而且不必实现var elements: [Any] { get }
在扩展名中NSMutableSet
和NSMutableOrderedSet
, 但我想这是不可避免的?
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: MutableSet { private let _add: (Any) -> () private let _makeIterator: () -> AnyIterator private var _getElements: () -> [Any] private var _getCount: () -> Int func add(_ element: Any) { _add(element) } func makeIterator() -> AnyIterator { return _makeIterator() } var count: Int { return _getCount() } var elements: [Any] { return _getElements() } init(_ 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 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
和subscript
能力,但是我只能使用func
(名为getCount
)这样做丑陋,而不是var
。 这就是我正在使用的:
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: MutableSet { private var _getCount: () -> Int private var _getElements: () -> [Any] private let _add: (Any) -> () private let _makeIterator: () -> AnyIterator func getElements() -> [Any] { return _getElements() } func add(_ element: Any) { _add(element) } func makeIterator() -> AnyIterator { return _makeIterator() } func getCount() -> Int { return _getCount() } init(_ 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 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]
在协议中,而不是函数?
几乎所有“我如何使用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: MutableSet { private let _add: (Any) -> () func add(_ element: Any) { _add(element) } private let _makeIterator: () -> AnyIterator func makeIterator() -> AnyIterator { return _makeIterator() } init(_ 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) }
原则上还有另一种方法,即直接使用现有的“协议,它允许我添加元素并迭代使用元素。” 这是两个协议: SetAlgebra & Sequence
。 在实践中,我发现要获得NSMutableSet
或NSOrderedSet
以符合SetAlgebra
……令人讨厌。 NSMutableSet
基本上在Swift 3中被破坏。它在各个地方接受Any
,但被定义为在AnyHashable
。 基本代码不起作用:
let s = NSMutableSet() let t = NSMutableSet() s.union(t)
但那是因为你不应该使用NSMutableSet
。 它自动桥接到Set
,你应该使用Set
代替。 Set
确实符合SetAlgebra & Sequence
所以没问题。
但后来我们来到NSOrderedSet
。 这很难融入Swift(这就是为什么基金会团队推迟了这么久)。 这真的是IMO类型的一团糟,每次我尝试使用它时,我都会将它拉出来,因为它不适合任何东西。 (尝试使用NSFetchedResultsController在“有序关系”中使用顺序。)坦率地说,最好的方法是将其包含在结构中,并使该结构符合SetAlgebra & Sequence
。
但是如果你不这样做(或者只是摆脱有序集合,就像我最终总是那样),那么类型擦除几乎是你唯一的工具。