我想在Swift中存储一个弱引用数组。数组本身不应该是弱引用——它的元素应该是。我认为Cocoa NSPointerArray提供了一个非类型安全的版本。


当前回答

这不是我的解决方案。我是在苹果开发者论坛上找到它的。

@GoZoner有一个很好的答案,但它崩溃了Swift编译器。

下面是一个弱对象容器的版本,它不会使当前发布的编译器崩溃。

struct WeakContainer<T where T: AnyObject> {
    weak var _value : T?

    init (value: T) {
        _value = value
    }

    func get() -> T? {
        return _value
    }
}

然后你可以创建一个这些容器的数组:

let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]

其他回答

函数式样式包装器呢?

class Class1 {}

func captureWeakly<T> (_ target:T) -> (() -> T?) where T: AnyObject {
    return { [weak target] in
        return target
    }
}

let obj1 = Class1()
let obj2 = Class1()
let obj3 = Class1()
let captured1 = captureWeakly(obj1)
let captured2 = captureWeakly(obj2)
let captured3 = captureWeakly(obj3)

只需调用返回的闭包来检查目标是否仍然存在。

let isAlive = captured1() != nil
let theValue = captured1()!

你可以把闭包存储到一个数组中。

let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])

您可以通过映射调用闭包来检索弱捕获的值。

let values = Array(array1.map({ $0() }))

实际上,你不需要一个函数来创建闭包。直接捕获一个对象。

let captured3 = { [weak obj3] in return obj3 }

我有同样的想法来创建带有泛型的弱容器。 因此,我为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协议,那么应用程序将崩溃,并给出详细的原因。但我现在找不到更好的解决办法。

最初的解决方案是这样定义WeakSet:

class WeakSet<ObjectType: AnyObject>: SequenceType {}

但是在这种情况下,WeakSet不能用协议初始化:

protocol MyDelegate : AnyObject {
    func doWork()
}

let weakSet = WeakSet<MyDelegate>()

目前以上代码无法编译(Swift 2.1, Xcode 7.1)。 这就是为什么我放弃符合AnyObject并使用fatalError()断言添加额外的保护。

函数式编程方法

不需要额外的课程。

简单地定义一个闭包数组()-> Foo?并使用[weak foo]将foo实例捕获为弱实例。

let foo = Foo()

var foos = [() -> Foo?]()
foos.append({ [weak foo] in return foo })

foos.forEach { $0()?.doSomething() }

可以围绕Array创建包装器。或者使用这个库https://github.com/NickRybalko/WeakPointerArray let array =弱指针数组<AnyObject>() 它是类型安全的。

现有的WeakContainer示例很有帮助,但它并不能真正帮助在现有的快速容器(如列表和字典)中使用弱引用。

如果您想使用List方法,如contains,那么WeakContainer将需要实现Equatable。因此,我添加了允许WeakContainer是相等的代码。

如果您想在字典中使用WeakContainer,我还将其设置为可哈希的,以便可以将其用作字典键。

我还将其重命名为WeakObject,以强调这仅适用于类类型,并将其与WeakContainer示例区分开来:

struct WeakObject<TYPE where TYPE:AnyObject> : Equatable, Hashable
{
    weak var _value : TYPE?
    let _originalHashValue : Int

    init (value: TYPE)
    {
        _value = value

        // We keep around the original hash value so that we can return it to represent this
        // object even if the value became Nil out from under us because the object went away.
        _originalHashValue = ObjectIdentifier(value).hashValue
    }

    var value : TYPE?
    {
        return _value
    }

    var hashValue: Int
    {
        return _originalHashValue
    }
}

func ==<T>(lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool
{
    if lhs.value == nil  &&  rhs.value == nil {
        return true
    }
    else if lhs.value == nil  ||  rhs.value == nil {
        return false
    }

    // If the objects are the same, then we are good to go
    return lhs.value! === rhs.value!
}

这允许你做一些很酷的事情,比如使用弱引用字典:

private var m_observerDict : Dictionary<WeakObject<AnyObject>,FLObservationBlock> = Dictionary()

func addObserver( observer:AnyObject, block:FLObservationBlock )
{
    let weakObserver = WeakObject(value:observer)
    m_observerDict[weakObserver] = block
}


func removeObserver( observer:AnyObject )
{
    let weakObserver = WeakObject(value:observer)
    m_observerDict.removeValueForKey(weakObserver)
}