¿Cómo declaro una serie de referencias débiles en Swift?

Resuelto Bill asked hace 10 años • 19 respuestas

Me gustaría almacenar una serie de referencias débiles en Swift. La matriz en sí no debería ser una referencia débil: sus elementos deberían serlo. Creo que Cocoa NSPointerArrayofrece una versión de esto que no es segura para tipos.

Bill avatar Jun 10 '14 02:06 Bill
Aceptado

Cree un contenedor genérico como:

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

Agregue instancias de esta clase a su matriz.

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

Al definir, Weakpuede utilizar structo class.

Además, para ayudar a cosechar el contenido de la matriz, puede hacer algo como lo siguiente:

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

El uso de AnyObjectlo anterior debería reemplazarse con T- pero no creo que el lenguaje Swift actual permita una extensión definida como tal.

GoZoner avatar Jun 09 '2014 20:06 GoZoner

Puede utilizar NSHashTable con débilObjectsHashTable.NSHashTable<ObjectType>.weakObjectsHashTable()

Para rápido 3:NSHashTable<ObjectType>.weakObjects()

Referencia de clase NSHashTable

Disponible en OS X v10.5 y posteriores.

Disponible en iOS 6.0 y posteriores.

Thierry avatar Nov 24 '2014 15:11 Thierry

Un enfoque de programación funcional

No se necesitan clases adicionales.

Simplemente defina una serie de cierres () -> Foo?y capture la instancia de foo como débil usando [weak foo].

let foo = Foo()

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

foos.forEach { $0()?.doSomething() }
frouo avatar Mar 16 '2020 14:03 frouo

Es un poco tarde para la fiesta, pero prueba el mío. Lo implementé como un conjunto, no como una matriz.

Conjunto de objetos débiles

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

Uso

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]

Tenga en cuenta que WeakObjectSet no aceptará el tipo String sino NSString. Porque el tipo String no es AnyType. Mi versión rápida es Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29).

El código se puede obtener de Gist. https://gist.github.com/codelynx/30d3c42a833321f17d39

** AGREGADO EN NOVIEMBRE DE 2017

Actualicé el código a 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) })
    }
}

Como mencionó gokeji, descubrí que NSString no se desasignará según el código en uso. Me rasqué la cabeza y escribí la clase MyString de la siguiente manera.

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

Luego reemplácelo NSStringcon MyStringasí. Entonces es extraño decir que funciona.

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]

Luego encontré una página extraña que puede estar relacionada con este problema.

La referencia débil conserva NSString desasignado (solo XC9 + iOS Sim)

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

Dice que el problema es RESOLVED, pero me pregunto si todavía está relacionado con este problema. De todos modos, las diferencias de comportamiento entre MyString o NSString están más allá de este contexto, pero agradecería que alguien resolviera este problema.

Kaz Yoshikawa avatar Mar 23 '2016 16:03 Kaz Yoshikawa