¿Cómo declaro una serie de referencias débiles en Swift?
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 NSPointerArray
ofrece una versión de esto que no es segura para tipos.
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, Weak
puede utilizar struct
o 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 AnyObject
lo anterior debería reemplazarse con T
- pero no creo que el lenguaje Swift actual permita una extensión definida como tal.
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.
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() }
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 NSString
con MyString
así. 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.