В Swift 3 я хотел бы иметь возможность создать протокол, который позволяет мне добавлять элементы и выполнять итерации с использованием for element in
. Протокол должен работать как с NSMutableSet
, так и с NSMutableOrderedSet
(поскольку они не наследуются от одного и того же класса).
Я знаю, что есть веские причины, по которым NSMutableSet
и NSMutableOrderedSet
не наследуются от одного и того же класса, это объясняется здесь и здесь.
Но я хочу создать протокол, который использует только часть всех методов внутри 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<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
из Element
, Iterator
и т. д. Я также пробовал этот ответ, он у меня не работает .
EDIT 2: ответ на мой вопрос в EDIT 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<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
EDIT 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<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]
в протоколе вместо функций?
var elements: [Any] { get }
в протоколMutableSet
, а затем переменнуюsubscript(position: Int) -> Any
. Но для этого требуется дополнительный код в расширенииNSMutableSet
:extension NSMutableSet: MutableSet { var elements: [Any] { return allObjects } }
- person Sajjon   schedule 22.09.2016