Pertanyaan Penggunaan sebagai tipe konkret yang sesuai dengan protokol AnyObject tidak didukung


Saya menggunakan Swift 2 dan menggunakan WeakContainer sebagai cara untuk menyimpan sekumpulan objek lemah, seperti NSHashTable.weakObjectsHashTable()

struct WeakContainer<T: AnyObject> {
    weak var value: T?
}

public protocol MyDelegate : AnyObject {

}

Kemudian di ViewController saya, saya menyatakan

public var delegates = [WeakContainer<MyDelegate>]

Tapi itu salah

Menggunakan MyDelegate sebagai jenis konkret yang sesuai dengan protokol AnyObject tidak didukung

Saya melihat bahwa kesalahannya adalah itu WeakContainer punya value anggota dinyatakan sebagai weak, jadi T diharapkan menjadi objek. Tapi saya juga menyatakan MyDelegate sebagai AnyObjectjuga. Bagaimana cara mengatasi ini?


32
2017-09-27 12:34


asal


Jawaban:


Saya memiliki ide yang sama untuk menciptakan wadah yang lemah dengan obat generik.
Sebagai hasilnya saya membuat bungkus untuk NSHashTable dan melakukan beberapa solusi untuk kesalahan kompilator Anda.

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?
        }
    }
}

Pemakaian:

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()
}

Itu bukan solusi terbaik, karena WeakSet dapat diinisialisasi dengan tipe apa pun, dan jika jenis ini tidak sesuai AnyObject protokol maka aplikasi akan macet. Tapi saya tidak melihat solusi yang lebih baik sekarang.


11
2017-11-14 05:32



Saya mengalami masalah yang sama ketika saya mencoba mengimplementasikan kontainer yang lemah. Seperti yang ditunjukkan oleh @plivesey dalam komentar di atas, ini sepertinya adalah a bug di Swift 2.2 / Xcode 7.3, tetapi itu diharapkan bekerja.

Namun, masalah tidak terjadi pada beberapa protokol Yayasan. Sebagai contoh, ini mengkompilasi:

let container = WeakContainer<NSCacheDelegate>()

Saya menemukan bahwa ini berfungsi untuk protokol yang ditandai dengan @objc atribut. Anda dapat menggunakan ini sebagai solusi:

Solusi 1

@objc
public protocol MyDelegate : AnyObject { }

let container = WeakContainer<MyDelegate>() // No compiler error

Karena ini dapat menyebabkan masalah lain (beberapa jenis tidak dapat diwakili dalam Objective-C), berikut adalah pendekatan alternatif:

Solusi 2

Jatuhkan AnyObject persyaratan dari penampung, dan berikan nilai ke AnyObject secara internal.

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

Peringatan: Menyimpan nilai yang sesuai T tetapi tidak AnyObjectgagal.


15
2018-04-28 11:27



Mengapa Anda mencoba menggunakan obat generik? Saya akan menyarankan melakukan hal berikut:

import Foundation
import UIKit

protocol MyDelegate : AnyObject {

}

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

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

Ada juga NSValue's nonretainedObject


2
2017-11-10 03:45



Masalah Anda adalah itu WeakContainer membutuhkan tipe generiknya T menjadi subtipe AnyObject - Sebuah protocol pernyataan tidak subtipe dari AnyObject. Anda memiliki empat opsi:

  1. Bukannya menyatakan WeakContainer<MyDelegate> gantilah dengan sesuatu yang benar-benar diimplementasikan MyDelegate. Pendekatan Swift-y untuk ini adalah dengan menggunakan AnyX pola: struct AnyMyDelegate : MyDelegate { ... }

  2. Menetapkan MyDelegate menjadi 'kelas terikat' sebagai protocol MyDelegate : class { ... }

  3. Membubuhi keterangan MyDelegate dengan @obj yang, pada dasarnya, membuatnya 'berkelas'

  4. Mereformulasi WeakContainer untuk tidak membutuhkan tipe generiknya untuk mewarisi AnyObject. Anda akan sulit sekali membuat ini berfungsi karena Anda membutuhkan properti yang dinyatakan sebagai weak var dan ada batasan untuk jenis apa yang diterima oleh weak var - yang mana AnyObject pada dasarnya.


1
2018-04-28 14:05



Jika Protokol Anda dapat ditandai sebagai @obj maka Anda dapat menggunakan kode di bawah ini

protocol Observerable {

    associatedtype P : AnyObject

    var delegates: NSHashTable<P> { get }
}

@objc protocol MyProtocol {

    func someFunc()

}

class SomeClass : Observerable {

    var delegates = NSHashTable<MyProtocol>.weakObjects()

}

1
2018-04-09 08:42



Berikut adalah implementasi WeakSet di Swift murni (tanpa NSHashTable).

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
2018-05-02 17:43