Ich möchte eine Reihe schwacher Referenzen in Swift speichern. Das Array selbst sollte keine schwache Referenz sein - seine Elemente sollten es sein. Ich denke, Cocoa NSPointerArray
bietet eine nicht typsichere Version davon.
Ich möchte eine Reihe schwacher Referenzen in Swift speichern. Das Array selbst sollte keine schwache Referenz sein - seine Elemente sollten es sein. Ich denke, Cocoa NSPointerArray
bietet eine nicht typsichere Version davon.
Antworten:
Erstellen Sie einen generischen Wrapper als:
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
Fügen Sie Ihrem Array Instanzen dieser Klasse hinzu.
class Stuff {}
var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]
Bei der Definition können Weak
Sie entweder struct
oder verwenden class
.
Um das Ernten von Array-Inhalten zu erleichtern, können Sie Folgendes tun:
extension Array where Element:Weak<AnyObject> {
mutating func reap () {
self = self.filter { nil != $0.value }
}
}
Die Verwendung von AnyObject
oben sollte durch ersetzt werden T
- aber ich denke nicht, dass die aktuelle Swift-Sprache eine als solche definierte Erweiterung zulässt.
Stuff
durch ein Protokoll ersetzen . siehe diese verwandte Frage
Sie können die NSHashTable mit schwachObjectsHashTable verwenden. NSHashTable<ObjectType>.weakObjectsHashTable()
Für Swift 3: NSHashTable<ObjectType>.weakObjects()
Verfügbar in OS X 10.5 und höher.
Verfügbar in iOS 6.0 und höher.
Any
aber nicht AnyObject
, wie Protokolle.
MyProtocol: class
und NSHashTable<MyProtocol>.weakObjects()
. "'NSHashTable' erfordert, dass 'MyProtocol' ein Klassentyp ist.
Es ist etwas spät für die Party, aber probieren Sie meine. Ich habe als Set kein Array implementiert.
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]
Beachten Sie, dass WeakObjectSet keinen String-Typ, sondern NSString verwendet. Weil der String-Typ kein AnyType ist. Meine schnelle Version ist Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
.
Code kann von Gist abgerufen werden. https://gist.github.com/codelynx/30d3c42a833321f17d39
** HINZUGEFÜGT IM NOV.2017
Ich habe den Code auf Swift 4 aktualisiert
// 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) })
}
}
Wie Gokeji erwähnte, habe ich herausgefunden, dass NSString aufgrund des verwendeten Codes nicht freigegeben wird. Ich kratzte mir am Kopf und schrieb die MyString-Klasse wie folgt.
// 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
}
}
Dann ersetzen Sie NSString
mit MyString
so. Dann seltsam zu sagen, dass es funktioniert.
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]
Dann fand ich eine seltsame Seite, die möglicherweise mit diesem Problem zusammenhängt.
Schwache Referenz behält freigegebenen NSString bei (nur XC9 + iOS Sim)
https://bugs.swift.org/browse/SR-5511
Es heißt, das Problem sei, RESOLVED
aber ich frage mich, ob dies noch mit diesem Problem zusammenhängt. Wie auch immer, Verhaltensunterschiede zwischen MyString oder NSString liegen außerhalb dieses Kontexts, aber ich würde mich freuen, wenn jemand dieses Problem herausfinden würde.
nil
Werte aus dem internen zu entfernen Set
. Deshalb habe ich eine reap()
in der oberen Antwort erwähnte Funktion hinzugefügt und sichergestellt, dass sie bei reap()
jedem WeakObjectSet
Zugriff aufgerufen wird.
nil
nicht mehr wird
NSString
dies nicht der Fall ist.
UnsafeMutablePointer<T>(&object)
, zufällig ändern kann (dasselbe gilt für withUnsafePointer
). Ich verwende jetzt eine Version, die von a unterstützt wird NSHashTable
: gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d .
Das ist nicht meine Lösung. Ich habe es in den Apple Developer Forums gefunden .
@GoZoner hat eine gute Antwort, aber es stürzt den Swift-Compiler ab.
Hier ist eine Version eines Containers für schwache Objekte, die den aktuell veröffentlichten Compiler nicht zum Absturz bringt.
struct WeakContainer<T where T: AnyObject> {
weak var _value : T?
init (value: T) {
_value = value
}
func get() -> T? {
return _value
}
}
Sie können dann ein Array dieser Container erstellen:
let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
EXC_BAD_ACCESS
für mich. Mit Klasse funktioniert gut
Sie können dies tun, indem Sie ein Wrapper-Objekt erstellen, das einen schwachen Zeiger enthält.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
Und diese dann im Array verwenden
var weakThings = WeakThing<Foo>[]()
class
, um weak
vars zu benutzen
protocol Protocol : class { ... }
Wie wäre es mit funktionalem Wrapper?
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)
Rufen Sie einfach die zurückgegebene Schließung an, um zu überprüfen, ob das Ziel noch am Leben ist.
let isAlive = captured1() != nil
let theValue = captured1()!
Und Sie können diese Verschlüsse in einem Array speichern.
let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])
Und Sie können die schwach erfassten Werte abrufen, indem Sie die Aufrufe der Schließungen zuordnen.
let values = Array(array1.map({ $0() }))
Eigentlich brauchen Sie keine Funktion, um einen Abschluss zu machen. Erfassen Sie einfach ein Objekt direkt.
let captured3 = { [weak obj3] in return obj3 }
var array: [(x: Int, y: () -> T?)]
. Genau das, wonach ich gesucht habe.
let values = Array(array1.map({ $0() })) part
. Da dies kein Array von Abschlüssen mit schwachen Referenzen mehr ist, bleiben die Werte erhalten, bis die Zuordnung dieses Arrays aufgehoben wird. Wenn ich richtig liege, ist es wichtig zu beachten, dass Sie dieses Array niemals so beibehalten sollten, self.items = Array(array1.map({ $0() }))
da dies den Zweck übertrifft.
Ich hatte die gleiche Idee, einen schwachen Container mit Generika zu erstellen.
Als Ergebnis habe ich einen Wrapper erstellt für 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?
}
}
}
Verwendung:
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()
}
Es ist nicht die beste Lösung, da WeakSet
es mit jedem Typ initialisiert werden kann. Wenn dieser Typ nicht dem AnyObject
Protokoll entspricht, stürzt die App aus detaillierten Gründen ab. Aber ich sehe momentan keine bessere Lösung.
Die ursprüngliche Lösung bestand darin, Folgendes zu definieren WeakSet
:
class WeakSet<ObjectType: AnyObject>: SequenceType {}
In diesem Fall WeakSet
kann jedoch nicht mit dem Protokoll initialisiert werden:
protocol MyDelegate : AnyObject {
func doWork()
}
let weakSet = WeakSet<MyDelegate>()
Derzeit kann der obige Code nicht kompiliert werden (Swift 2.1, Xcode 7.1).
Aus diesem Grund habe ich die Anpassung eingestellt AnyObject
und zusätzliche Wachen mit fatalError()
Behauptungen hinzugefügt .
struct WeakObject<Object: AnyObject> { weak var object: Object? }
@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) }
}
}
class Class1 { // or struct
@WeakElements var weakObjectsArray = [UIView?]() // Use like regular array. With any objects
func test() {
weakObjectsArray.append(UIView())
weakObjectsArray.forEach { print($0) }
}
}
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) })
}
}
class Class2 { // or struct
var weakObjectsArray = WeakObjectsArray<UIView>() // Use like regular array. With any objects
func test() {
weakObjectsArray.append(UIView())
weakObjectsArray.forEach { print($0) }
}
}
Vergessen Sie nicht, den Lösungscode einzufügen
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>() }
protocol TP: class { } class TC<TYPE> where TYPE: TP { var a = WeakObjectsArray<TYPE>() // Use like regular array. With any objects var weakObjectsArray = [TYPE?]() }
delegates
. Dann hätten Sie einige View Controller, die diese Funktionalität nutzen möchten. Sie würden erwarten, anzurufen MyManager.delegates.append(self)
. Wenn MyManager
es jedoch an einen generischen Typ gebunden ist, ist dies nicht sehr brauchbar.
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())
Das vorhandene Beispiel für den WeakContainer ist hilfreich, aber es hilft nicht wirklich, schwache Referenzen in vorhandenen schnellen Containern wie Listen und Wörterbüchern zu verwenden.
Wenn Sie List-Methoden wie Contain verwenden möchten, muss der WeakContainer Equatable implementieren. Also habe ich den Code hinzugefügt, damit der WeakContainer gleichwertig ist.
Für den Fall, dass Sie den WeakContainer in Wörterbüchern verwenden möchten, habe ich ihn auch hashbar gemacht, damit er als Wörterbuchschlüssel verwendet werden kann.
Ich habe es auch in WeakObject umbenannt, um zu betonen, dass dies nur für Klassentypen gilt, und um es von den WeakContainer-Beispielen zu unterscheiden:
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!
}
Auf diese Weise können Sie einige coole Dinge tun, z. B. ein Wörterbuch mit schwachen Referenzen verwenden:
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)
}
Hier ist , wie @ GoZoner die große Antwort machen entsprechen Hashable
, so dass es indiziert werden können in Container - Objekte wie: Set
, Dictionary
, Array
etc.
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
}
Da NSPointerArray
das meiste davon bereits automatisch erledigt wird, habe ich das Problem gelöst, indem ich einen typsicheren Wrapper dafür erstellt habe, der in anderen Antworten viel von der Boilerplate vermeidet:
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
}
Anwendungsbeispiel:
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
Es ist mehr Arbeit im Voraus, aber die Verwendung im Rest Ihres Codes ist IMO viel sauberer. Wenn Sie es Array-ähnlicher machen möchten, können Sie sogar Subskription implementieren, es zu einem machen SequenceType
usw. (aber mein Projekt benötigt nur append
und forEach
daher habe ich nicht den genauen Code zur Hand).
Eine weitere Lösung für das gleiche Problem ... Der Schwerpunkt dieses Problems liegt auf dem Speichern eines schwachen Verweises auf ein Objekt, aber auch auf dem Speichern einer Struktur.
[Ich bin nicht sicher, wie nützlich es ist, aber es hat eine Weile gedauert, bis die Syntax richtig war]
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
Sie könnten einen Wrapper erstellen Array
. Oder verwenden Sie diese Bibliothek https://github.com/NickRybalko/WeakPointerArray.
let array = WeakPointerArray<AnyObject>()
Sie ist typsicher.
Andere Antworten haben den generischen Blickwinkel abgedeckt. Ich dachte, ich würde einen einfachen Code teilen, der das abdecktnil
Winkel .
Ich wollte ein statisches Array (gelegentlich lesen) aller Label
s, die derzeit in der App vorhanden sind, wollte es aber nicht sehennil
, wo sich die alten befanden.
Nichts Besonderes, das ist mein Code ...
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
anstelle von filter
& map
?
Ich habe dies auf die Arbeit von @Eonil gestützt, da ich die Strategie der schwachen Verschlussbindung liebte, aber ich wollte keinen Funktionsoperator für eine Variable verwenden, da sie sich äußerst kontraintuitiv anfühlte
Was ich stattdessen getan habe, ist wie folgt:
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
}
}
}
Auf diese Weise können Sie Folgendes tun:
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
Das ist meine Lösung:
- -
// 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)) }
}
}
Dies ist eine typsichere Sammlung, die Container mit schwachen Objekten enthält. Außerdem werden beim Zugriff automatisch keine Container / Wrapper entfernt.
Beispiel:
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() }
}
}
Die benutzerdefinierte Sammlung 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 }
}
}
Was ist mit einem funktionalen Ansatz ?
let observers = [() -> Observer?]()
observers.append({ [weak anObserver] in return anObserver })
Dies ist die Hauptidee. Fügen Sie dann eine beliebige Komfortlogik hinzu, um zu verfolgen, was sich im Array befindet. Zum Beispiel könnte man den gleichen Ansatz mit einem Wörterbuch in Betracht ziehen, bei dem der Schlüssel verwendet wird, um herauszufinden, was sich darin befindet.