web-dev-qa-db-ja.com

プロトコルAnyObjectに準拠した具象型としての使用はサポートされていません

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を使用することはサポートされていません

エラーは、WeakContainervalueとして宣言されたweakメンバーを持っているというエラーです。したがって、Tはオブジェクトであると予想されます。ただし、MyDelegateAnyObjectとして宣言します。これを回避する方法は?

33
onmyway133

ジェネリックでウィークコンテナーを作成することも同じ考えでした。
その結果、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プロトコルに準拠していない場合、アプリがクラッシュします。しかし、今のところこれ以上の解決策はありません。

12
Vlad Papko

弱いコンテナを実装しようとしたときに、同じ問題に遭遇しました。上記のコメントで@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に準拠しているがAnyObjectsではない値の保存は失敗します。

16
Theo

ジェネリックを使用しようとしているのはなぜですか?私は以下を行うことをお勧めします:

import Foundation
import UIKit

protocol MyDelegate : AnyObject {

}

class WeakContainer : AnyObject {
    weak var value: MyDelegate?
}

class ViewController: UIViewController {
    var delegates = [WeakContainer]()
}

NSValuenonretainedObjectもあります

3
Kyle Redfearn

プロトコルを@objとしてマークできる場合は、以下のコードを使用できます

protocol Observerable {

    associatedtype P : AnyObject

    var delegates: NSHashTable<P> { get }
}

@objc protocol MyProtocol {

    func someFunc()

}

class SomeClass : Observerable {

    var delegates = NSHashTable<MyProtocol>.weakObjects()

}
2
Misha Vyrko

あなたの問題は、WeakContainerがそのジェネリック型TAnyObjectのサブタイプであることを必要とすることです-protocol宣言is notAnyObjectのサブタイプ。次の4つのオプションがあります。

  1. WeakContainer<MyDelegate>を宣言する代わりに、MyDelegateを実際に実装するものに置き換えます。これに対するSwift-yのアプローチは、AnyXパターンを使用することです:struct AnyMyDelegate : MyDelegate { ... }

  2. MyDelegateprotocol MyDelegate : class { ... }として「クラスバインド」するように定義します

  3. MyDelegate@objの注釈を付けます。これにより、本質的に「クラスバウンド」になります

  4. WeakContainernotに再公式化すると、そのジェネリック型がAnyObjectから継承する必要があります。 weak varとして宣言されたプロパティが必要であり、weak varが受け入れるタイプには制限があるため、これを機能させるのは困難です。これは、本質的にAnyObjectです。

1
GoZoner

これは、純粋な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
            }
        }
    }
}
0