2016-01-21 44 views
9

我做一個協議:斯威夫特擴展存儲的協議

protocol SomeProtocol { 
    func getData() -> String 
} 

我做符合它的結構:

struct SomeStruct: SomeProtocol { 
    func getData() -> String { 
     return "Hello" 
    } 
} 

現在我希望每個UIViewController有一個叫source屬性,所以我可以做類似...

class MyViewController : UIViewController { 
    override func viewDidLoad() { 
     self.title = source.getData() 
    } 
} 

爲了達到這個目的,我創建了一個協議到d efine屬性:

protocol SomeProtocolInjectable { 
    var source: SomeProtocol! { get set } 
} 

現在我只需要到視圖控制器與此屬性擴展:

extension UIViewController: SomeProtocolInjectable { 
    // ??? 
} 

我如何可以破解一起存儲的屬性,將與協議類型的工作?

什麼沒有奏效:

任何其他建議?

+0

會使用靜態屬性爲你工作嗎? –

+0

這是一個很好的解決方法,但理想情況下不同的視圖控制器會有不同的「源」。如果你想把它作爲答案,如果沒有更好的事情發生,我會在一兩天內接受它。 –

+0

請參閱下面的答案 - 您可以使用某些* proxy *類型爲每個實例實現不同的'source' ... –

回答

3

任何協議對象可被轉換成一種類型的擦除類。建立一個AnySomeProtocol並存儲它。

private var sourceKey: UInt8 = 0 

final class AnySomeProtocol: SomeProtocol { 
    func getData() -> String { return _getData() } 
    init(_ someProtocol: SomeProtocol) { _getData = someProtocol.getData } 
    private let _getData:() -> String 
} 

extension UIViewController: SomeProtocolInjectable { 
    var source: SomeProtocol! { 
     get { 
      return objc_getAssociatedObject(self, &sourceKey) as? SomeProtocol 
     } 
     set(newValue) { 
      objc_setAssociatedObject(self, &sourceKey, AnySomeProtocol(newValue), .OBJC_ASSOCIATION_RETAIN) 
     } 
    } 
} 

class MyViewController : UIViewController { 
    override func viewDidLoad() { 
     self.title = source.getData() 
    } 
} 

調用者只能使用它來訪問協議方法。你不能用as強制回到原來的類型,但是你應該避免這種情況。作爲一個便箋,我真的會推薦讓source返回SomeProtocol?而不是SomeProtocol!。這裏沒有什麼承諾source將被設置。你甚至沒有設置它,直到viewDidLoad

+0

如果我正確地理解了這一點,它會從任何實現的'SomeProtocol'複製狀態。但除此之外,這不會有效地用'AnySomeProtocol'替代'SomeStruct'嗎? 'SomeStruct'將有不同​​的實現。例如,一種類型可能會從Web服務獲取數據,而另一種類型則可能來自Core Data。 –

+0

這不會複製狀態。它轉發給結構的實現。 _getData不是一個字符串。這是一個返回String的函數。即使結構不可訪問,閉包捕獲結構(或捕獲通過實現此協議的任何其他內容)。這非常像stdlib中的AnySequence或AnyGenerator。見http://robnapier.net/erasure –

+0

謝謝!精彩的博客文章也是如此。 –

3

可以砍周圍具有靜態和視圖控制器hash

+0

請注意,這依賴於無證行爲。它恰好工作,因爲UIViewController通過返回它的地址指針來實現散列,但這不是承諾的。兩個視圖控制器被允許具有相同的散列(他們可能不會)。相反,您可以在指向對象的指針上進行索引:'Unmanaged.passUnretained(self).toOpaque()'(這是一個有效的字典鍵,但指針完全不安全)。請注意,即使在視圖控制器被銷燬後,這種方法也不會釋放存儲的數據。 –

+0

聰明!但我同意@RobNapier,我想避免依賴無證散列行爲。 +1雖然創造力。 –

0

如何添加一個默認的實現爲getData(),具有對協議的虛擬結構的實現,並使用它作爲source變量的默認值:

protocol SomeProtocol { 
    func getData() -> String 
} 

extension SomeProtocol { 
    func getData() -> String { 
     return "Hello" 
    } 
} 

protocol SomeProtocolInjectable { 
    var source: SomeProtocol { get set } 
} 

struct DummyProtocolImplementation: SomeProtocol { 

} 

class MyViewController : UIViewController { 
    var _source: SomeProtocol = DummyProtocolImplementation() 

    override func viewDidLoad() { 
     self.title = source.getData() 
    } 
} 

extension MyViewController: SomeProtocolInjectable { 
    var source: SomeProtocol { get { return _source } set { _source = newValue } } 
} 

我冒昧地延長MyViewController,而不是無論如何,後者並不知道該協議。

+0

這需要在每個視圖控制器子類上聲明一個屬性,這正是我試圖避免的。 –

+0

如果您從'MyViewController'繼承所有控制器,則不行 – Cristik