Swift 2を使用しており、WeakContainerをNSHashTable.weakObjectsHashTable()
のように、弱いオブジェクトのセットを格納する方法として使用しています
struct WeakContainer<T: AnyObject> {
weak var value: T?
}
public protocol MyDelegate : AnyObject {
}
次に、私のViewControllerで、私は宣言します
public var delegates = [WeakContainer<MyDelegate>]
しかし、それはエラーです
プロトコルAnyObjectに準拠する具象型としてMyDelegateを使用することはサポートされていません
エラーは、WeakContainer
がvalue
として宣言されたweak
メンバーを持っているというエラーです。したがって、T
はオブジェクトであると予想されます。ただし、MyDelegate
もAnyObject
として宣言します。これを回避する方法は?
ジェネリックでウィークコンテナーを作成することも同じ考えでした。
その結果、NSHashTable
のラッパーを作成し、コンパイラエラーのいくつかの回避策を実行しました。
class WeakSet<ObjectType>: SequenceType {
var count: Int {
return weakStorage.count
}
private let weakStorage = NSHashTable.weakObjectsHashTable()
func addObject(object: ObjectType) {
guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
weakStorage.addObject(object as? AnyObject)
}
func removeObject(object: ObjectType) {
guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
weakStorage.removeObject(object as? AnyObject)
}
func removeAllObjects() {
weakStorage.removeAllObjects()
}
func containsObject(object: ObjectType) -> Bool {
guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
return weakStorage.containsObject(object as? AnyObject)
}
func generate() -> AnyGenerator<ObjectType> {
let enumerator = weakStorage.objectEnumerator()
return anyGenerator {
return enumerator.nextObject() as! ObjectType?
}
}
}
使用法:
protocol MyDelegate : AnyObject {
func doWork()
}
class MyClass: AnyObject, MyDelegate {
fun doWork() {
// Do delegated work.
}
}
var delegates = WeakSet<MyDelegate>()
delegates.addObject(MyClass())
for delegate in delegates {
delegate.doWork()
}
WeakSet
は任意のタイプで初期化できるため、これは最良のソリューションではありません。このタイプがAnyObject
プロトコルに準拠していない場合、アプリがクラッシュします。しかし、今のところこれ以上の解決策はありません。
弱いコンテナを実装しようとしたときに、同じ問題に遭遇しました。上記のコメントで@pliveseyが指摘しているように、これはSwift 2.2/Xcode 7.3の バグ のようですが、 動作することが期待されています 。
ただし、問題は一部のFoundationプロトコルでは発生しません。たとえば、これはコンパイルします:
let container = WeakContainer<NSCacheDelegate>()
これが@objc
属性でマークされたプロトコルで機能することがわかりました。これを回避策として使用できます。
回避策1
@objc
public protocol MyDelegate : AnyObject { }
let container = WeakContainer<MyDelegate>() // No compiler error
これは他の問題につながる可能性があるため(一部のタイプはObjective-Cで表現できない)、別のアプローチを次に示します。
回避策2
AnyObject
要件をコンテナーから削除し、内部で値をAnyObject
にキャストします。
struct WeakContainer<T> {
private weak var _value:AnyObject?
var value: T? {
get {
return _value as? T
}
set {
_value = newValue as? AnyObject
}
}
}
protocol MyDelegate : AnyObject { }
var container = WeakContainer<MyDelegate>() // No compiler error
警告:T
に準拠しているがAnyObject
sではない値の保存は失敗します。
ジェネリックを使用しようとしているのはなぜですか?私は以下を行うことをお勧めします:
import Foundation
import UIKit
protocol MyDelegate : AnyObject {
}
class WeakContainer : AnyObject {
weak var value: MyDelegate?
}
class ViewController: UIViewController {
var delegates = [WeakContainer]()
}
NSValue
のnonretainedObject
もあります
プロトコルを@objとしてマークできる場合は、以下のコードを使用できます
protocol Observerable {
associatedtype P : AnyObject
var delegates: NSHashTable<P> { get }
}
@objc protocol MyProtocol {
func someFunc()
}
class SomeClass : Observerable {
var delegates = NSHashTable<MyProtocol>.weakObjects()
}
あなたの問題は、WeakContainer
がそのジェネリック型T
がAnyObject
のサブタイプであることを必要とすることです-protocol
宣言is notAnyObject
のサブタイプ。次の4つのオプションがあります。
WeakContainer<MyDelegate>
を宣言する代わりに、MyDelegate
を実際に実装するものに置き換えます。これに対するSwift-yのアプローチは、AnyX
パターンを使用することです:struct AnyMyDelegate : MyDelegate { ... }
MyDelegate
をprotocol MyDelegate : class { ... }
として「クラスバインド」するように定義します
MyDelegate
に@obj
の注釈を付けます。これにより、本質的に「クラスバウンド」になります
WeakContainer
をnotに再公式化すると、そのジェネリック型がAnyObject
から継承する必要があります。 weak var
として宣言されたプロパティが必要であり、weak var
が受け入れるタイプには制限があるため、これを機能させるのは困難です。これは、本質的にAnyObject
です。
これは、純粋なSwift(NSHashTableなし))でのWeakSetの実装です。
internal struct WeakBox<T: AnyObject> {
internal private(set) weak var value: T?
private var pointer: UnsafePointer<Void>
internal init(_ value: T) {
self.value = value
self.pointer = unsafeAddressOf(value)
}
}
extension WeakBox: Hashable {
var hashValue: Int {
return self.pointer.hashValue
}
}
extension WeakBox: Equatable {}
func ==<T>(lhs: WeakBox<T>, rhs: WeakBox<T>) -> Bool {
return lhs.pointer == rhs.pointer
}
public struct WeakSet<Element>: SequenceType {
private var boxes = Set<WeakBox<AnyObject>>()
public mutating func insert(member: Element) {
guard let object = member as? AnyObject else {
fatalError("WeakSet's member (\(member)) must conform to AnyObject protocol.")
}
self.boxes.insert(WeakBox(object))
}
public mutating func remove(member: Element) {
guard let object = member as? AnyObject else {
fatalError("WeakSet's member (\(member)) must conform to AnyObject protocol.")
}
self.boxes.remove(WeakBox(object))
}
public mutating func removeAll() {
self.boxes.removeAll()
}
public func contains(member: Element) -> Bool {
guard let object = member as? AnyObject else {
fatalError("WeakSet's member (\(member)) must conform to AnyObject protocol.")
}
return self.boxes.contains(WeakBox(object))
}
public func generate() -> AnyGenerator<Element> {
var generator = self.boxes.generate()
return AnyGenerator {
while(true) {
guard let box = generator.next() else {
return nil
}
guard let element = box.value else {
continue
}
return element as? Element
}
}
}
}