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


当前回答

在很多情况下,返回一个可取消的选项会更简洁。这允许调用站点决定何时显式地销毁值(以及通过解压缩隐式地销毁值):


public protocol Cancellable {
    func cancel()
}

private struct MyValue: Identifiable {
    let id: String
    // ...
}

private class CancellationHandler: Cancellable {
    let handler: () -> ()
    init(handler: @escaping () -> ()) { self.handler = handler }
    func cancel() { handler() }
    deinit { handler() }
}

public class Container {
    private var array = [MyType]()

    public func add() -> Cancellable {
        let value = MyValue(...)
        array.append(value)
        return CancellationHandler {
            array.removeFirst(where: { $0.id == value.id })
        }
    }
}

let cancellable = container.add()

// Both cancellable.cancel() and the cancellable descoping 
// will call the `cancel` function, removing the value from array.

其他回答

函数式样式包装器呢?

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 }

我基于@Eonil的工作,因为我喜欢闭包弱绑定策略,但我不想为变量使用函数操作符,因为这感觉非常违反直觉

相反,我所做的是:

class Weak<T> where T: AnyObject {
    fileprivate var storedWeakReference: ()->T? = { return nil }

    var value: T? {
        get {
            return storedWeakReference()
        }
    }

    init(_ object: T) {
        self.storedWeakReference = storeWeakReference(object)
    }

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

这样你可以做一些事情,比如:

var a: UIViewController? = UIViewController()
let b = Weak(a)
print(a) //prints Optional(<UIViewController: 0xSomeAddress>)
print(b.value) //prints Optional(<UIViewController: 0xSomeAddress>)
a = nil
print(a) //prints nil
print(b.value) //prints nil

同样的问题还有另一种解决方案……它的重点是存储对对象的弱引用,但也允许存储结构。

[我不确定它有多有用,但它确实花了一段时间来纠正语法]

class WeakWrapper : Equatable {
    var valueAny : Any?
    weak var value : AnyObject?

    init(value: Any) {
        if let valueObj = value as? AnyObject {
            self.value = valueObj
        } else {
            self.valueAny = value
        }
    }

    func recall() -> Any? {
        if let value = value {
            return value
        } else if let value = valueAny {
            return value
        }
        return nil
    }
}


func ==(lhs: WeakWrapper, rhs: WeakWrapper) -> Bool {
    return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}



class Stuff {}
var weakArray : [WeakWrapper] = [WeakWrapper(value: Stuff()), WeakWrapper(value: CGRectZero)]

extension Array where Element : WeakWrapper  {

    mutating func removeObject(object: Element) {
        if let index = self.indexOf(object) {
            self.removeAtIndex(index)
        }
    }

    mutating func compress() {
        for obj in self {
            if obj.recall() == nil {
                self.removeObject(obj)
            }
        }
    }


}

weakArray[0].recall()
weakArray[1].recall() == nil
weakArray.compress()
weakArray.count

函数式编程方法

不需要额外的课程。

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

let foo = Foo()

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

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

创建一个通用包装器,如下:

class Weak<T: AnyObject> {
  weak var value : T?
  init (value: T) {
    self.value = value
  }
}

将该类的实例添加到数组中。

class Stuff {}
var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]

定义Weak时,可以使用struct或class。

同样,为了帮助获取数组内容,你可以做一些如下的事情:

extension Array where Element:Weak<AnyObject> {
  mutating func reap () {
    self = self.filter { nil != $0.value }
  }
}

上面使用的AnyObject应该替换为T -但我不认为当前的Swift语言允许这样定义扩展。