Протокол, соединяющий NSMutableSet и NSMutableOrderedSet вместе
В 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
и т.д. Я также попробовал этот ответ, он не работает для меня.
РЕДАКТИРОВАТЬ 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<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
а также 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]
в протоколе, а не функции?
1 ответ
Практически каждый ответ "как мне использовать 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
в разных местах, но определяется как оконченный AnyHashable
, Основной код не работает:
let s = NSMutableSet()
let t = NSMutableSet()
s.union(t)
Но это потому, что вы не должны использовать NSMutableSet
, Он автоматически соединен с Set
и вы должны использовать Set
вместо. А также Set
соответствует SetAlgebra & Sequence
так что это было бы хорошо.
Но потом мы приходим к NSOrderedSet
, Это очень трудно соединить со Свифтом (вот почему команда Фонда так долго откладывала его). Это действительно беспорядок типа IMO, и каждый раз, когда я пытался использовать это, я заканчивал тем, что вытаскивал это, потому что это не играет хорошо ни с чем. (Попробуйте использовать NSFetchedResultsController, чтобы использовать порядок в "упорядоченных отношениях".) Откровенно говоря, лучше всего заключить его в структуру и привести в соответствие эту структуру SetAlgebra & Sequence
,
Но если вы не пойдете по этому пути (или просто избавитесь от упорядоченных наборов, как я всегда в конечном итоге сделаю), то стирание типов в значительной степени ваш единственный инструмент.