2016-09-29 43 views
10

我想實現一個簡單的多委託情況:使用協議的具體類型,符合「AnyObject」不支持

protocol Subscribable: class { 
    associatedtype Subscriber: AnyObject 
    var subscribers: NSHashTable<Subscriber> { get } 
} 

protocol ControllerSubscriber: class { 
    func controllerDidSomething() 
} 

class Controller: Subscribable { 
    typealias Subscriber = ControllerSubscriber 
    var subscribers = NSHashTable<Subscriber>.weakObjects() // Error 
} 

Error: Using 'ControllerSubscriber' as a concrete type conforming to protocol 'AnyObject' is not supported.

我的問題是:

  • 這個錯誤究竟意味着什麼?
  • 我試圖做的事情的底層概念是什麼?
  • 這是爲什麼「不支持」?

當然,我該如何解決這個問題?從實際的解決方案來看,這不是一種解決方法。

我很難理解Swift的泛型系統。我似乎不斷遇到像這樣的看似簡單的情況。我只想把一個符合協議的東西變成另一個東西:(我想知道我的思路出了什麼地方,所以我可以修復它,並且再也不會看到這些錯誤了。

還有this related question但請請注意,答案只給出瞭解決方法,沒有解釋或解決方案

+0

查看問題與解答[協議不符合自己?](http://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-it-自己)(它應該回答你的第一個3(甚至可能最後)問題)和[無法使用協議作爲關聯類型在另一個協議在Swift](http://stackoverflow.com/questions/37360114/unable-to-use-protocol-as-associatedtype-in​​-another-protocol-in -swift)這應該回答你最後一個問題。 – Hamish

+0

整個問題是協議並不總是符合自己的 - 因此您不能使用抽象類型(如ControllerSubscriber)作爲符合AnyObject的具體類型。 – Hamish

+1

遲到回覆:感謝您的鏈接,這是一個很好的起點!從我讀過的所有東西看來,它似乎是「這是因爲它是沒有人知道爲什麼。」老實說,這些東西真的讓我的熱情下降了。我沒有看到自己正在爲使用這種訂戶模式的所有情況編寫刪除類型的包裝器。無論如何。在第二個鏈接中,您提出了幾個備選解決方案。在這種情況下,除了類型擦除的包裝之外,您是否還看到過其他替代方案?再次感謝您的回覆。 – tombardey

回答

2

這可能是不公平的,應該把這個問題歸咎於Swift,關於類型的推理似乎是我們首先必須習慣的一些元藝術(除非你有過去30年一直在C++標準委員會工作:-)

原來你的問題與你選擇的NSHashTable有關,作爲數據str保存你的subscribers。下面將通過很少的更改編譯:

protocol Subscribable: class { 
    associatedtype Subscriber 
    var subscribers: [Subscriber?] { get } 
} 

protocol ControllerSubscriber: class { 
    func controllerDidSomething() 
} 

class Controller: Subscribable { 
    typealias Subscriber = ControllerSubscriber 
    var subscribers = [Subscriber?]() 
} 

但是,它缺乏weak語義,是不是真的有用呢。 subscribers列表作爲屬性展示,必須由客戶直接操作。此外,Subscribable的每個實現都必須實現其自己的通知機制,並且幾乎沒有任何邏輯由此方法集中。從技術上講,你可以這樣使用它:

class Controller: Subscribable { 
    typealias Subscriber = ControllerSubscriber 
    var subscribers = [Subscriber?]() 

    func notify() { 
     for case let subscriber? in subscribers { 
      subscriber.controllerDidSomething() 
     } 
    } 
} 

var controller = Controller() 

class IWillSubscribe : ControllerSubscriber { 
    func controllerDidSomething() { 
     print("I got something") 
    } 
} 

controller.subscribers.append(IWillSubscribe()) 
controller.notify() 

但這不是很實用,也不是非常可讀。這將是一個可接受的解決方案(因爲它是唯一一個)直到Java 7,但即使在Java 8中(在Swift中也是如此),我們希望將通知邏輯作爲默認實現封裝到Subscribable協議中,但那將是另一篇文章。

由於您選擇實施subscribers作爲NSHashTable(這裏可能有一個ARC理由希望弱引用),似乎涉及到一些Objective-C欺騙。多次實驗(終於找到第四個答案this question後,我得到了以下工作:

protocol Subscribable: class { 
    associatedtype Subscriber : AnyObject 
    var subscribers: NSHashTable<Subscriber> { get } 
} 

@objc protocol ControllerSubscriber: class { 
    func controllerDidSomething() 
} 

class Controller: Subscribable { 
    typealias Subscriber = ControllerSubscriber 
    var subscribers = NSHashTable<Subscriber>.weakObjects() 

    func notify() { 
     for subscriber in subscribers.allObjects { 
      subscriber.controllerDidSomething() 
     } 
    } 
} 

var controller = Controller() 

class IWillSubscribe : ControllerSubscriber { 
    func controllerDidSomething() { 
     print("I got something") 
    } 
} 

let iDoSubscribe = IWillSubscribe() 
controller.subscribers.add(iDoSubscribe) 
controller.notify() 

這幾乎是等同於原來的(與它周圍的一些證據),因爲它似乎Objective-C的@protocol s爲。不是相當於與Swift 相同,但Swift 可以實際上也做。

有這雖然相當多的精妙之處,只有allObjects作品沒有類型擦除,您值得信賴的objectEnumerator只返回Any?,這是一個愚蠢的動物得到任何東西。還請注意,

let iDoSubscribe = IWillSubscribe() 

是有用的。起初,我試圖

controller.subscribers.add(IWillSubscribe()) 

這實際上補上一的subscriberscount,但任何試圖重複(如一個應該從沒有提及任何其它地方weak參考期待)走了。

非常遲到的回答是已經太長時間,只是爲了證明這是仍然一個問題,即使斯威夫特3.也許這將得到更好一旦this Jira ticket得到解決。

相關問題