2016-09-20 62 views
1

Swift 3,我希望能够创建一个协议,允许我添加元素和迭代通过使用for element in。该协议应该可以在NSMutableSetNSMutableOrderedSet上运行(因为它们不会从同一个类继承)。协议桥接NSMutableSet和NSMutableOrderedSet在一起

我知道有,为什么NSMutableSetNSMutableOrderedSet不会从同一类继承好的理由,解释herehere

但我想创建一个协议,只使用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?我应该怎么做?我尝试过typealiasassociatedtypeElementIterator等的各种组合。我也试过this answer它不适合我。

编辑2:答案我自己的问题在EDIT 1

var count: Int { get }使用此解决方案的工作,不知道这是否是虽然最好的一个...也将是不错的不必在NSMutableSetNSMutableOrderedSet,的扩展中实现var elements: [Any] { get },但我想这是不可避免的?

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:跟进问题 使用优秀的答案被@抢劫,纳皮尔与type erasure技术我已经扩展了protocol MutableSetcount,也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

'_getCount = {ms.count}' –

+0

谢谢!是的,我使用过,看到我的编辑2,就是这样的解决方案!嗯...那么解决方案呢?用封闭包装...(方法签名) – Sajjon

+0

是的;这是一种将属性转换为函数的简单方法。尽管关于元素我不明白第二个问题。 –

回答

2

几乎所有“我如何用PAT(协议与关联类型)......”的答案是“把它放在一个盒子里”。那个盒子是type eraser。在你的情况下,你想要一个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) 
} 

原则还有另一种方式,这是直来直去现行的“协议,让我通过使用在元素添加元素和迭代。”这是两个协议:SetAlgebra & Sequence。在实践中,我发现要么NSMutableSetNSOrderedSet符合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。但如果你不这样做(或者只是摆脱有序集合,就像我总是最终这样做),然后键入擦除几乎是你唯一的工具。

+0

你摇滚先生! :D真棒!非常感谢!我实际上是使用NSManagedObject进行深层副本的扩展,类似于https://gist.github.com/advantis/7642084,但是我希望最小化代码依赖于如果关系被排序:) – Sajjon

+0

我写了一个后续代码,如何使用下标能力和'count'属性来代替'func getCount()'解决方案?任何帮助深表感谢! – Sajjon

+0

我在编辑2中回答了我自己的问题(在编辑1中)并回答了我的问题。 – Sajjon