Using as a concrete type conforming to protocol AnyObject is not supported

前端 未结 6 1314
时光取名叫无心
时光取名叫无心 2020-12-05 00:12

I\'m using Swift 2 and using WeakContainer as a way to store a set of weak objects, much like NSHashTable.weakObjectsHashTable()

struct WeakCont         


        
相关标签:
6条回答
  • 2020-12-05 00:49

    I had the same idea to create weak container with generics.
    As result I created wrapper for NSHashTable and did some workaround for your compiler error.

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

    Usage:

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

    It's not the best solution, because WeakSet can be initialized with any type, and if this type doesn't conform to AnyObject protocol then app will crash. But I don't see any better solution right now.

    0 讨论(0)
  • 2020-12-05 00:52

    If your Protocol can be marked as @obj then you can use code below

    protocol Observerable {
    
        associatedtype P : AnyObject
    
        var delegates: NSHashTable<P> { get }
    }
    
    @objc protocol MyProtocol {
    
        func someFunc()
    
    }
    
    class SomeClass : Observerable {
    
        var delegates = NSHashTable<MyProtocol>.weakObjects()
    
    }
    
    0 讨论(0)
  • 2020-12-05 00:52

    Here is my implementation of WeakSet in pure Swift (without 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 讨论(0)
  • 2020-12-05 01:00

    Why are you trying to use generics? I would suggest doing the following:

    import Foundation
    import UIKit
    
    protocol MyDelegate : AnyObject {
    
    }
    
    class WeakContainer : AnyObject {
        weak var value: MyDelegate?
    }
    
    class ViewController: UIViewController {
        var delegates = [WeakContainer]()
    }
    

    There is also NSValue's nonretainedObject

    0 讨论(0)
  • 2020-12-05 01:01

    Your issue is that WeakContainer requires its generic type T to be a subtype of AnyObject - a protocol declaration is not a subtype of AnyObject. You have four options:

    1. Instead of declaring WeakContainer<MyDelegate> replace it with something that actually implements MyDelegate. The Swift-y approach for this is to use the AnyX pattern: struct AnyMyDelegate : MyDelegate { ... }

    2. Define MyDelegate to be 'class bound' as protocol MyDelegate : class { ... }

    3. Annotate MyDelegate with @obj which, essentially, makes it 'class bound'

    4. Reformulate WeakContainer to not require its generic type to inherit from AnyObject. You'll be hard pressed to make this work because you need a property declared as weak var and there are limitation as to what types are accepted by weak var - which are AnyObject essentially.

    0 讨论(0)
  • 2020-12-05 01:07

    I ran into the same problem when I tried to implement weak containers. As @plivesey points out in a comment above, this seems to be a bug in Swift 2.2 / Xcode 7.3, but it is expected to work.

    However, the problem does not occur for some Foundation protocols. For example, this compiles:

    let container = WeakContainer<NSCacheDelegate>()
    

    I found out that this works for protocols marked with the @objc attribute. You can use this as a workaround:

    Workaround 1

    @objc
    public protocol MyDelegate : AnyObject { }
    
    let container = WeakContainer<MyDelegate>() // No compiler error
    

    As this can lead to other problems (some types cannot be represented in Objective-C), here is an alternative approach:

    Workaround 2

    Drop the AnyObject requirement from the container, and cast the value to AnyObject internally.

    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
    

    Caveat: Setting a value that conforms to T, but is not an AnyObject, fails.

    0 讨论(0)
提交回复
热议问题