Протокол, соединяющий 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,

Но если вы не пойдете по этому пути (или просто избавитесь от упорядоченных наборов, как я всегда в конечном итоге сделаю), то стирание типов в значительной степени ваш единственный инструмент.

Другие вопросы по тегам