如何在Swift中声明弱引用数组?


179

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


1
怎样使一个容器对象弱引用另一个对象,然后对它们进行数组处理呢?(如果您没有更好的答案)
nielsbot 2014年

1
你为什么不使用NSPointerArray?
巴斯蒂安2014年

@nielsbot这是一个旧的obj-c解决方案:)要使其快速化,它应该是一个通用对象!:)但是,真正的问题是当释放所引用的对象时如何从数组中删除对象。
苏珊(Sulthan)2014年

2
是的,我更喜欢带有参数化类型的东西。我想我可以围绕NSPointerArray进行参数化包装,但是想看看是否有其他选择。
比尔

6
就像另一个选项一样,NSHashTable也存在。从本质上讲,它是一个NSSet,可让您指定应如何引用其中包含的对象。
Mick MacCallum 2014年

Answers:


154

创建通用包装,如下所示:

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,可以使用structclass

另外,为了帮助获取数组内容,您可以按照以下方式进行操作:

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

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


11
当释放值后,如何从数组中删除包装对象?
苏珊(Sulthan)2014年

9
是的,它使编译器崩溃了。
GoZoner 2014年

5
请在新问题中发布您的问题代码;当它可能是您的代码时,没有理由给我答复!
GoZoner 2015年

2
@EdGamble提供的代码按原样工作,但是如果您用Stuff协议替换该类,则该代码将失败;看到这个相关问题
Theo

2
一个结构会更好,因为它会保留在堆栈中,而不需要堆提取。
KPM

60

您可以将NSHashTable与weakObjectsHashTable一起使用。 NSHashTable<ObjectType>.weakObjectsHashTable()

对于Swift 3: NSHashTable<ObjectType>.weakObjects()

NSHashTable类参考

在OS X v10.5和更高版本中可用。

在iOS 6.0及更高版本中可用。


最佳答案,不要为包装纸浪费时间!
拉米斯2015年

1
这是聪明的,但像GoZoner的答案,这并不与那些类型的工作Any,但并不AnyObject如协议。
亚伦·布拉格

@SteveWilford但是,协议可以由类实现,这将使其成为引用类型
Aaron Brager

4
协议可以扩展类,然后可以将其用作弱类(例如,协议MyProtocol:class)
Yasmin Tiomkin

1
我收到了MyProtocol: class和的编译器错误NSHashTable<MyProtocol>.weakObjects()。“'NSHashTable'要求'MyProtocol'为类类型。–
Greg

14

参加聚会有点晚,但是请尝试我的聚会。我实现为集合而不是数组。

弱对象集

class WeakObject<T: AnyObject>: Equatable, Hashable {
    weak var object: T?
    init(object: T) {
        self.object = object
    }

    var hashValue: Int {
        if let object = self.object { return unsafeAddressOf(object).hashValue }
        else { return 0 }
    }
}

func == <T> (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
    return lhs.object === rhs.object
}


class WeakObjectSet<T: AnyObject> {
    var objects: Set<WeakObject<T>>

    init() {
        self.objects = Set<WeakObject<T>>([])
    }

    init(objects: [T]) {
        self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
    }

    var allObjects: [T] {
        return objects.flatMap { $0.object }
    }

    func contains(object: T) -> Bool {
        return self.objects.contains(WeakObject(object: object))
    }

    func addObject(object: T) {
        self.objects.unionInPlace([WeakObject(object: object)])
    }

    func addObjects(objects: [T]) {
        self.objects.unionInPlace(objects.map { WeakObject(object: $0) })
    }
}

用法

var alice: NSString? = "Alice"
var bob: NSString? = "Bob"
var cathline: NSString? = "Cathline"

var persons = WeakObjectSet<NSString>()
persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObjects([alice!, cathline!])
print(persons.allObjects) // [Alice, Cathline, Bob]

alice = nil
print(persons.allObjects) // [Cathline, Bob]

bob = nil
print(persons.allObjects) // [Cathline]

注意,WeakObjectSet不会采用String类型,而是NSString。因为,String类型不是AnyType。我的快速版本是Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)

可以从Gist获取代码。 https://gist.github.com/codelynx/30d3c42a833321f17d39

**新增于2017年11月

我将代码更新为Swift 4

// Swift 4, Xcode Version 9.1 (9B55)

class WeakObject<T: AnyObject>: Equatable, Hashable {
    weak var object: T?
    init(object: T) {
        self.object = object
    }

    var hashValue: Int {
        if var object = object { return UnsafeMutablePointer<T>(&object).hashValue }
        return 0
    }

    static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
        return lhs.object === rhs.object
    }
}

class WeakObjectSet<T: AnyObject> {
    var objects: Set<WeakObject<T>>

    init() {
        self.objects = Set<WeakObject<T>>([])
    }

    init(objects: [T]) {
        self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
    }

    var allObjects: [T] {
        return objects.flatMap { $0.object }
    }

    func contains(_ object: T) -> Bool {
        return self.objects.contains(WeakObject(object: object))
    }

    func addObject(_ object: T) {
        self.objects.formUnion([WeakObject(object: object)])
    }

    func addObjects(_ objects: [T]) {
        self.objects.formUnion(objects.map { WeakObject(object: $0) })
    }
}

正如gokeji所述,我发现NSString不会根据使用的代码进行释放。我挠头,按如下方式编写MyString类。

// typealias MyString = NSString
class MyString: CustomStringConvertible {
    var string: String
    init(string: String) {
        self.string = string
    }
    deinit {
        print("relasing: \(string)")
    }
    var description: String {
        return self.string
    }
}

然后更换NSStringMyString这样的。然后奇怪地说它可行。

var alice: MyString? = MyString(string: "Alice")
var bob: MyString? = MyString(string: "Bob")
var cathline: MyString? = MyString(string: "Cathline")

var persons = WeakObjectSet<MyString>()

persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObjects([alice!, cathline!])
print(persons.allObjects) // [Alice, Cathline, Bob]

alice = nil
print(persons.allObjects) // [Cathline, Bob]

bob = nil
print(persons.allObjects) // [Cathline]

然后我发现一个奇怪的页面可能与此问题有关。

弱引用保留释放的NSString(仅限XC9 + iOS Sim)

https://bugs.swift.org/browse/SR-5511

它说的是问题,RESOLVED但我想知道这是否仍然与此问题有关。无论如何,MyString或NSString之间的行为差​​异不在此上下文范围内,但是如果有人发现了此问题,我将不胜感激。


我已经为我的项目采用了此解决方案。很好!只是一个建议,该解决方案似乎并未nil从internal中删除值Set。因此,我添加了reap()最上面答案中提到的函数,并确保reap()每次WeakObjectSet访问时都调用。
gokeji

嗯,等等,由于某种原因,这在Swift 4 / iOS 11中不起作用。似乎弱引用在值变得nil不再时不会立即被释放
gokeji

1
我将代码更新为Swift4,请参见答案的下半部分。我似乎NSString有一些释放问题,但是它仍然可以在您的自定义类对象上工作。
Kaz Yoshikawa

非常感谢您关注@KazYoshikawa并更新了答案!后来我还意识到,自定义类有效,而NSString无效。
gokeji

2
我的经验是,返回的指针UnsafeMutablePointer<T>(&object)可以随机更改(与相同withUnsafePointer)。我现在使用一个由以下版本支持的版本NSHashTablegist.github.com/simonseyer/cf73e733355501405982042f760d2a7d
simonseyer

12

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

@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]

1
奇怪,但不再适用于结构。说EXC_BAD_ACCESS我。课堂
教学

6
结构是值类型,不应与它们一起使用。它在运行时崩溃而不是编译时错误的事实是编译器错误。
David Goodine '16

10

您可以通过创建一个包装器对象来保存弱指针来做到这一点。

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

然后在数组中使用它们

var weakThings = WeakThing<Foo>[]()

必须是一个class在使用weak瓦尔
比尔

3
谁说的?上面的代码对我来说很好。唯一的要求是变弱的对象需要是一个类,而不是持有弱引用的对象
Joshua Weinberg 2014年

抱歉。我可能发誓我只是收到一条编译器消息,内容为“不能在结构中使用弱变量”。您是正确的-可以编译。
比尔

5
@JoshuaWeinberg如果Foo是协议,该怎么办?
onmyway133 2015年

@ onmyway133 AFAIK如果声明协议仅由类实现,它将起作用。 protocol Protocol : class { ... }
olejnjak '16

8

功能样式包装器如何?

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 }

3
问题是如何创建弱对象的数组(或说Set)。
David H

使用此解决方案,您甚至可以创建具有多个值的数组,例如var array: [(x: Int, y: () -> T?)]。正是我要找的东西。
jboi

1
@DavidH我更新了答案以回答问题。我希望这有帮助。
翁尼尔2016年

喜欢这种方法,而且我认为它非常聪明。我使用这种策略进行了类实现。谢谢!
Ale Ravasio

不太确定let values = Array(array1.map({ $0() })) part。由于这不再是带有弱引用的闭包数组,因此将保留值,直到该数组被释放为止。如果我是正确的,那么需要注意的是,切勿保留此数组,否则将无法self.items = Array(array1.map({ $0() }))达到目的。
Matic Oblak

7

我有相同的想法,用泛型创建弱容器。
结果,我为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()断言的附加防护措施的原因。



6

细节

  • Swift 5.1,Xcode 11.3.1

struct WeakObject<Object: AnyObject> { weak var object: Object? }

选项1

@propertyWrapper
struct WeakElements<Collect, Element> where Collect: RangeReplaceableCollection, Collect.Element == Optional<Element>, Element: AnyObject {
    private var weakObjects = [WeakObject<Element>]()

    init(wrappedValue value: Collect) { save(collection: value) }

    private mutating func save(collection: Collect) {
        weakObjects = collection.map { WeakObject(object: $0) }
    }

    var wrappedValue: Collect {
        get { Collect(weakObjects.map { $0.object }) }
        set (newValues) { save(collection: newValues) }
    }
}

选项1的用法

class Class1 { // or struct
    @WeakElements var weakObjectsArray = [UIView?]() // Use like regular array. With any objects

    func test() {
        weakObjectsArray.append(UIView())
        weakObjectsArray.forEach { print($0) }
    }
}

选项2

struct WeakObjectsArray<Object> where Object: AnyObject {
    private var weakObjects = [WeakObject<Object>]()
}

extension WeakObjectsArray {
    typealias SubSequence = WeakObjectsArray<Object>
    typealias Element = Optional<Object>
    typealias Index = Int
    var startIndex: Index { weakObjects.startIndex }
    var endIndex: Index { weakObjects.endIndex }
    func index(after i: Index) -> Index { weakObjects.index(after: i) }
    subscript(position: Index) -> Element {
        get { weakObjects[position].object }
        set (newValue) { weakObjects[position] = WeakObject(object: newValue) }
    }
    var count: Int { return weakObjects.count }
    var isEmpty: Bool { return weakObjects.isEmpty }
}

extension WeakObjectsArray: RangeReplaceableCollection {
    mutating func replaceSubrange<C : Collection>( _ subrange: Range<Index>, with newElements: C) where Element == C.Element {
        weakObjects.replaceSubrange(subrange, with: newElements.map { WeakObject(object: $0) })
    }
}

选项2的用法

class Class2 { // or struct
    var weakObjectsArray = WeakObjectsArray<UIView>() // Use like regular array. With any objects

    func test() {
        weakObjectsArray.append(UIView())
        weakObjectsArray.forEach { print($0) }
    }
}

完整样本

不要忘记粘贴解决方案代码

import UIKit

class ViewController: UIViewController {

    @WeakElements var weakObjectsArray = [UIView?]()
    //var weakObjectsArray = WeakObjectsArray<UIView>()

    override func viewDidLoad() {
        super.viewDidLoad()
        addSubviews()
    }

    private func printArray(title: String) {
        DispatchQueue.main.async {
            print("=============================\n\(title)\ncount: \(self.weakObjectsArray.count)")
            self.weakObjectsArray.enumerated().forEach { print("\($0) \(String(describing: $1))") }
        }
    }
}

extension ViewController {

    private func createRandomRectangleAndAdd(to parentView: UIView) -> UIView {
        let view = UIView(frame: CGRect(x: Int.random(in: 0...200),
                                        y: Int.random(in: 60...200),
                                        width: Int.random(in: 0...200),
                                        height: Int.random(in: 0...200)))
        let color = UIColor(red: CGFloat.random(in: 0...255)/255,
                            green: CGFloat.random(in: 0...255)/255,
                            blue: CGFloat.random(in: 0...255)/255,
                            alpha: 1)
        view.backgroundColor = color
        parentView.addSubview(view)
        return view
    }

    private func addSubviews() {
        (0...1).forEach { _ in addView() }
        addButtons()
    }

    private func createButton(title: String, frame: CGRect, action: Selector) -> UIButton {
        let button = UIButton(frame: frame)
        button.setTitle(title, for: .normal)
        button.addTarget(self, action: action, for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        return button
    }

    private func addButtons() {
        view.addSubview(createButton(title: "Add",
                                     frame: CGRect(x: 10, y: 20, width: 40, height: 40),
                                     action: #selector(addView)))

        view.addSubview(createButton(title: "Delete",
                                     frame: CGRect(x: 60, y: 20, width: 60, height: 40),
                                     action: #selector(deleteView)))

        view.addSubview(createButton(title: "Remove nils",
                                     frame: CGRect(x: 120, y: 20, width: 100, height: 40),
                                     action: #selector(removeNils)))
    }

    @objc func deleteView() {
        view.subviews.first { view -> Bool in return !(view is UIButton) }?
            .removeFromSuperview()

        printArray(title: "First view deleted")
    }

    @objc func addView() {
        weakObjectsArray.append(createRandomRectangleAndAdd(to: view))
        printArray(title: "View addded")
    }

    @objc func removeNils() {
        weakObjectsArray = weakObjectsArray.filter { $0 != nil }
        printArray(title: "Remove all nil elements in weakArray")
    }
}

我对这两个选项(以及许多其他选项)的问题是,这些类型的数组无法与协议一起使用。例如,它将无法编译:protocol TP: class { } class TC { var a = WeakArray<TP>() var b = WeakObjectsArray<TP>() }
Matic Oblak

@MaticOblak如何使用泛型? protocol TP: class { } class TC<TYPE> where TYPE: TP { var a = WeakObjectsArray<TYPE>() // Use like regular array. With any objects var weakObjectsArray = [TYPE?]() }
瓦西里·博德纳丘克

这个想法是该数组可以容纳实现相同类协议的不同类型的对象。通过使用泛型,可以将其锁定为单一类型。例如,假设有一个单例将此类数组保存为delegates。然后,您将有一些想要使用此功能的视图控制器。您可能会打电话给MyManager.delegates.append(self)。但是,如果MyManager锁定为某种通用类型,则该功能将不太可用。
Matic Oblak

@MaticOblak好。试试这个: protocol TP: class { } class MyManager { typealias Delegate = AnyObject & TP static var delegates = [Delegate?]() } class A: TP { } class B: TP { } //MyManager.delegates.append(A()) //MyManager.delegates.append(B())
Vasily Bodnarchuk

现在,您失去了数组的通用部分,这有点重要:)我感觉这是行不通的。Swift的局限性...
Matic Oblak

4

现有的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)
}

3

以下是如何让@ GoZoner最伟大的回答符合Hashable,因此它可以被索引在容器中的对象,如:SetDictionaryArray等。

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

    var hashValue : Int {
       // ObjectIdentifier creates a unique hashvalue for objects.
       return ObjectIdentifier(self.value).hashValue
    }
}

// Need to override so we can conform to Equitable.
private func == <T>(lhs: Weak<T>, rhs: Weak<T>) -> Bool {
    return lhs.hashValue == rhs.hashValue
}

3

由于NSPointerArray已经自动处理了大部分此类操作,因此我通过为其创建类型安全的包装程序解决了该问题,从而避免了其他答案中的许多重复步骤:

class WeakArray<T: AnyObject> {
    private let pointers = NSPointerArray.weakObjects()

    init (_ elements: T...) {
        elements.forEach{self.pointers.addPointer(Unmanaged.passUnretained($0).toOpaque())}
    }

    func get (_ index: Int) -> T? {
        if index < self.pointers.count, let pointer = self.pointers.pointer(at: 0) {
            return Unmanaged<T>.fromOpaque(pointer).takeUnretainedValue()
        } else {
            return nil
        }
    }
    func append (_ element: T) {
        self.pointers.addPointer(Unmanaged.passUnretained(element).toOpaque())
    }
    func forEach (_ callback: (T) -> ()) {
        for i in 0..<self.pointers.count {
            if let element = self.get(i) {
                callback(element)
            }
        }
    }
    // implement other functionality as needed
}

用法示例:

class Foo {}
var foo: Foo? = Foo()
let array = WeakArray(foo!)
print(array.get(0)) // Optional(Foo)
foo = nil
DispatchQueue.main.async{print(array.get(0))} // nil

前面需要做更多的工作,但是其余代码中的用法更干净。如果您想使其更像数组,甚至可以实现下标,将其设置为SequenceType等(但我的项目仅需要appendforEach所以我手边没有确切的代码)。


2

相同问题的另一种解决方案……这个问题的重点是存储对对象的弱引用,但也允许您存储结构。

[我不确定它有多有用,但是确实花了一些时间才能正确使用语法]

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


1

其他答案涵盖了泛型的角度。以为我会分享一些涵盖nil角度的简单代码。

我想要Label应用程序中当前存在的所有s 的静态数组(偶尔读取),但不想看到nils以前的旧数组。

没什么,这是我的代码...

public struct WeakLabel {
    public weak var label : Label?
    public init(_ label: Label?) {
        self.label = label
    }
}

public class Label : UILabel {
    static var _allLabels = [WeakLabel]()
    public static var allLabels:[WeakLabel] {
        get {
            _allLabels = _allLabels.filter{$0.label != nil}
            return _allLabels.filter{$0.label != nil}.map{$0.label!}
        }
    }
    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        Label._allLabels.append(WeakLabel(self))
    }
    public override init(frame: CGRect) {
        super.init(frame: frame)
        Label._allLabels.append(WeakLabel(self))
    }
}

那用&flatMap代替呢?filtermap
卢卡斯·库巴内克

0

我基于@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

0

这是我的解决方案:

  • 释放数组时清理数组,因为WeakObjectSet正在存储而不是从WeakObject中退出
  • 解决在Set中发现重复元素时的致命错误

-

// MARK: - WeakObjectSet 

public class WeakObject<T: AnyObject>: Equatable, Hashable {

    // MARK: Public propreties

    public weak var object: T?
    public var hashValue: Int {
        return self.identifier.hashValue
    }

    // MARK: Private propreties

    private let identifier: ObjectIdentifier

    // MARK: Initializer

    public init(object: T) {
        self.identifier = ObjectIdentifier(object)
        self.object = object
    }

    public static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
        return lhs.identifier == rhs.identifier
    }
}

// MARK: - WeakObjectSet

public class WeakObjectSet<T: AnyObject> {

    // MARK: Public propreties

    public var allObjects: [T] {
        return allSetObjects.compactMap { $0.object }
    }

    // MARK: Private propreties

    private var objects: Set<WeakObject<T>>
    private var allSetObjects: Set<WeakObject<T>> {
        get {
            objects = self.objects.filter { $0.object != nil }
            return objects
        }
        set {
            objects.formUnion(newValue.filter { $0.object != nil })
        }
    }

    // MARK: Initializer

    public init() {
        self.objects = Set<WeakObject<T>>([])
    }

    public init(objects: [T]) {
        self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
    }

    // MARK: Public function

    public func contains(_ object: T) -> Bool {
        return self.allSetObjects.contains(WeakObject(object: object))
    }

    public func addObject(_ object: T) {
        self.allSetObjects.insert(WeakObject(object: object))
    }

    public func addObjects(_ objects: [T]) {
        objects.forEach { self.allSetObjects.insert(WeakObject(object: $0)) }
    }
}

0

这是一个类型安全的集合,其中包含弱对象的容器。当访问它时,它也会自动删除零个容器/包装器。

例:

protocol SomeDelegate: class {
    func doSomething()
}

class SomeViewController: UIViewController {
    var delegates: WeakCollection<SomeDelegate> = []

    func someFunction(delegate: SomeDelegate) {
        delegates.append(delegate)
    }

    func runDelegates() {
        delegates.forEach { $0.doSomething() }
    }
}

自定义集合 https://gist.github.com/djk12587/46d85017fb3fad6946046925f36cefdc

import Foundation

/**
 Creates an array of weak reference objects.
 - Important:
    Because this is an array of weak objects, the objects in the array can be removed at any time.
    The collection itself will handle removing nil objects (garbage collection) via the private function cleanUpNilContainers()
 */

class WeakCollection<T>: RangeReplaceableCollection, ExpressibleByArrayLiteral {
    typealias Index = Int
    typealias Element = T
    typealias Iterator = IndexingIterator<[Element]>

    private var weakContainers: [WeakReferenceContainer]

    required convenience init(arrayLiteral: Element...) {
        self.init()
        self.weakContainers = WeakCollection.createWeakContainers(from: arrayLiteral)
    }

    required init() {
        weakContainers = []
    }

    required init<S>(_ elements: S) where S: Sequence, WeakCollection.Element == S.Element {
        self.weakContainers = WeakCollection.createWeakContainers(from: elements)
    }

    static private func createWeakContainers<S>(from weakCollection: S) -> [WeakReferenceContainer] where S: Sequence,
        WeakCollection.Element == S.Element {
            return weakCollection.compactMap { WeakReferenceContainer(value: $0 as AnyObject) }
    }

    func append<S>(contentsOf newElements: S) where S: Sequence, WeakCollection.Element == S.Element {
        self.weakContainers.append(contentsOf: WeakCollection.createWeakContainers(from: newElements))
    }

    var startIndex: Index {
        return references.startIndex
    }

    var endIndex: Index {
        return references.endIndex
    }

    func replaceSubrange<C, R>(_ subrange: R, with newElements: C) where
        C: Collection, R: RangeExpression, WeakCollection.Element == C.Element, WeakCollection.Index == R.Bound {
            weakContainers.replaceSubrange(subrange, with: WeakCollection.createWeakContainers(from: newElements))
    }

    func index(after i: Int) -> Int {
        return references.index(after: i)
    }

    func makeIterator() -> IndexingIterator<[Element]> {
        return references.makeIterator()
    }

    subscript(index: Int) -> Element {
        get {
            return references[index]
        }
        set {
            weakContainers[index] = WeakReferenceContainer(value: newValue as AnyObject)
        }
    }
}

extension WeakCollection {
    private class WeakReferenceContainer {
        private(set) weak var value: AnyObject?

        init(value: AnyObject?) {
            self.value = value
        }
    }

    private func cleanUpNilContainers() {
        weakContainers = weakContainers.compactMap { $0.value == nil ? nil : $0 }
    }

    private var references: [Element] {
        cleanUpNilContainers()
        return weakContainers.compactMap { $0.value as? T }
    }
}

0

怎么样一个功能性的做法

let observers = [() -> Observer?]()

observers.append({ [weak anObserver] in return anObserver })

这是主要思想,然后添加任何便捷逻辑来跟踪阵列中的内容。例如,可以考虑使用使用键作为字典的字典作为查找其中内容的方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.