2016-09-20 54 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