J'aimerais stocker un tableau de références faibles dans Swift. Le tableau lui-même ne doit pas être une référence faible - ses éléments doivent l'être. Je pense que Cocoa en NSPointerArray
propose une version non sécurisée.
J'aimerais stocker un tableau de références faibles dans Swift. Le tableau lui-même ne doit pas être une référence faible - ses éléments doivent l'être. Je pense que Cocoa en NSPointerArray
propose une version non sécurisée.
Réponses:
Créez un wrapper générique comme:
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
Ajoutez des instances de cette classe à votre tableau.
class Stuff {}
var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]
Lors de la définition, Weak
vous pouvez utiliser soit struct
ou class
.
De plus, pour aider à récolter le contenu du tableau, vous pouvez faire quelque chose du genre:
extension Array where Element:Weak<AnyObject> {
mutating func reap () {
self = self.filter { nil != $0.value }
}
}
L'utilisation de AnyObject
ci - dessus devrait être remplacée par T
- mais je ne pense pas que le langage Swift actuel autorise une extension définie comme telle.
Stuff
par un protocole; voir cette question connexe
Vous pouvez utiliser NSHashTable avec lowObjectsHashTable. NSHashTable<ObjectType>.weakObjectsHashTable()
Pour Swift 3: NSHashTable<ObjectType>.weakObjects()
Référence de classe NSHashTable
Disponible dans OS X v10.5 et versions ultérieures.
Disponible dans iOS 6.0 et versions ultérieures.
Any
pas AnyObject
, tels que les protocoles.
MyProtocol: class
et NSHashTable<MyProtocol>.weakObjects()
. "'NSHashTable' nécessite que 'MyProtocol' soit un type de classe.
Il est un peu tard pour la fête, mais essayez le mien. J'ai implémenté comme un ensemble et non un tableau.
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]
Attention, WeakObjectSet ne prendra pas le type String mais NSString. Parce que le type String n'est pas un AnyType. Ma version rapide est Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
.
Le code peut être récupéré à partir de Gist. https://gist.github.com/codelynx/30d3c42a833321f17d39
** AJOUTÉ EN NOV 2017
J'ai mis à jour le code vers 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) })
}
}
Comme gokeji l'a mentionné, j'ai compris que NSString ne serait pas désalloué en fonction du code utilisé. Je me suis gratté la tête et j'ai écrit la classe MyString comme suit.
// 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
}
}
Puis remplacez NSString
par MyString
comme ça. Puis étrange de dire que cela fonctionne.
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]
Ensuite, j'ai trouvé qu'une page étrange pouvait être liée à ce problème.
La référence faible conserve la NSString désallouée (XC9 + iOS Sim uniquement)
https://bugs.swift.org/browse/SR-5511
Il dit que le problème est, RESOLVED
mais je me demande si cela est toujours lié à ce problème. Quoi qu'il en soit, les différences de comportement entre MyString ou NSString sont au-delà de ce contexte, mais j'apprécierais que quelqu'un trouve ce problème.
nil
valeurs de l'interne Set
. J'ai donc ajouté une reap()
fonction mentionnée dans la première réponse, et je me suis assuré d'appeler à reap()
chaque fois que le WeakObjectSet
est accédé.
nil
plus
NSString
n'est pas le cas.
UnsafeMutablePointer<T>(&object)
peut changer de manière aléatoire (même chose avec withUnsafePointer
). J'utilise maintenant une version soutenue par NSHashTable
: gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d .
Ce n'est pas ma solution. Je l'ai trouvé sur les forums des développeurs Apple .
@GoZoner a une bonne réponse, mais il plante le compilateur Swift.
Voici une version d'un conteneur d'objets faibles qui ne plante pas le compilateur publié actuel.
struct WeakContainer<T where T: AnyObject> {
weak var _value : T?
init (value: T) {
_value = value
}
func get() -> T? {
return _value
}
}
Vous pouvez ensuite créer un tableau de ces conteneurs:
let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
EXC_BAD_ACCESS
pour moi. Avec la classe fonctionne très bien
Vous pouvez le faire en créant un objet wrapper pour contenir un pointeur faible.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
Et puis en les utilisant dans le tableau
var weakThings = WeakThing<Foo>[]()
class
pour utiliser weak
vars
protocol Protocol : class { ... }
Qu'en est-il de l'emballage de style fonctionnel?
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)
Appelez simplement la fermeture retournée pour vérifier que la cible est toujours en vie.
let isAlive = captured1() != nil
let theValue = captured1()!
Et vous pouvez stocker ces fermetures dans un tableau.
let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])
Et vous pouvez récupérer les valeurs faiblement capturées en mappant l'appel des fermetures.
let values = Array(array1.map({ $0() }))
En fait, vous n'avez pas besoin d'une fonction pour faire une fermeture. Capturez simplement un objet directement.
let captured3 = { [weak obj3] in return obj3 }
var array: [(x: Int, y: () -> T?)]
. Exactement, ce que je cherchais.
let values = Array(array1.map({ $0() })) part
. Comme il ne s'agit plus d'un tableau de fermetures avec des références faibles, les valeurs seront conservées jusqu'à ce que ce tableau soit désalloué. Si j'ai raison, il est important de noter que vous ne devriez jamais conserver ce tableau comme self.items = Array(array1.map({ $0() }))
cela dépasse l'objectif.
J'ai eu la même idée de créer un conteneur faible avec des génériques.
En conséquence, j'ai créé un wrapper pour 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?
}
}
}
Usage:
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()
}
Ce n'est pas la meilleure solution, car elle WeakSet
peut être initialisée avec n'importe quel type, et si ce type n'est pas conforme au AnyObject
protocole, l'application plantera avec une raison détaillée. Mais je ne vois pas de meilleure solution pour le moment.
La solution originale était de définir WeakSet
de cette manière:
class WeakSet<ObjectType: AnyObject>: SequenceType {}
Mais dans ce cas, WeakSet
ne peut pas être initialisé avec le protocole:
protocol MyDelegate : AnyObject {
func doWork()
}
let weakSet = WeakSet<MyDelegate>()
Actuellement, le code ci-dessus ne peut pas être compilé (Swift 2.1, Xcode 7.1).
C'est pourquoi j'ai abandonné la conformité AnyObject
et ajouté des gardes supplémentaires avec des fatalError()
affirmations.
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) }
}
}
n'oubliez pas de coller le code de la solution
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
. Ensuite, vous auriez un certain nombre de contrôleurs de vue qui aimeraient utiliser cette fonctionnalité. Vous vous attendez à appeler MyManager.delegates.append(self)
. Mais si MyManager
est verrouillé sur un type générique, ce n'est pas très utilisable.
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())
L'exemple existant de WeakContainer est utile, mais il n'aide pas vraiment à utiliser des références faibles dans des conteneurs rapides existants tels que les listes et les dictionnaires.
Si vous souhaitez utiliser des méthodes List telles que contains, le WeakContainer devra implémenter Equatable. J'ai donc ajouté le code pour permettre au WeakContainer d'être égalable.
Au cas où vous voudriez utiliser le WeakContainer dans les dictionnaires, je l'ai également rendu hachable afin qu'il puisse être utilisé comme clé de dictionnaire.
Je l'ai également renommé WeakObject pour souligner que ce n'est que pour les types de classe et pour le différencier des exemples 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!
}
Cela vous permet de faire des choses sympas comme utiliser un dictionnaire de références faibles:
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)
}
Voici comment faire @ grande réponse GoZoner conforme à Hashable
, donc il peut être indexé dans les objets Conteneur comme: 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
}
Comme cela NSPointerArray
gère déjà la plupart de cela automatiquement, j'ai résolu le problème en créant un emballage de type sécurisé, ce qui évite une grande partie du passe-partout dans d'autres réponses:
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
}
Exemple d'utilisation:
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
C'est plus de travail à l'avant, mais l'utilisation dans le reste de votre code est beaucoup plus propre à l'OMI. Si vous voulez le rendre plus semblable à un tableau, vous pouvez même implémenter un indice, en faire un SequenceType
, etc. (mais mon projet n'en a besoin que append
et forEach
je n'ai donc pas le code exact sous la main).
Encore une autre solution au même problème ... l'objectif de celui-ci est de stocker une référence faible à un objet, mais vous permet également de stocker une structure.
[Je ne sais pas à quel point il est utile, mais il a fallu un certain temps pour obtenir la bonne syntaxe]
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
Vous pouvez créer un wrapper autour Array
. Ou utilisez cette bibliothèque https://github.com/NickRybalko/WeakPointerArray
let array = WeakPointerArray<AnyObject>()
Il est de type sécurisé.
D'autres réponses ont couvert l'angle des génériques. Je pensais que je partagerais un code simple couvrant l' nil
angle.
Je voulais un tableau statique (lu occasionnellement) de tous les Label
s qui existent actuellement dans l'application, mais je ne voulais pas voir nil
où se trouvaient les anciens.
Rien d'extraordinaire, c'est mon 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
au lieu de filter
& map
?
Je me suis basé sur le travail de @Eonil, car j'aimais la stratégie de fermeture de liaison faible, mais je ne voulais pas utiliser un opérateur de fonction pour une variable, car cela me semblait extrêmement contre-intuitif
Ce que j'ai fait, à la place, est comme suit:
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
}
}
}
De cette façon, vous pouvez faire quelque chose comme:
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
C'est ma solution:
-
// 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)) }
}
}
Il s'agit d'une collection de type sécurisé qui contient des conteneurs d'objets faibles. Il supprime également automatiquement nil les conteneurs / wrappers lors de l'accès.
Exemple:
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() }
}
}
La collection personnalisée 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 }
}
}
Qu'en est-il d'une approche fonctionnelle ?
let observers = [() -> Observer?]()
observers.append({ [weak anObserver] in return anObserver })
C'est l'idée principale, puis ajoutez une logique de commodité pour garder une trace de ce qui se trouve dans le tableau. Par exemple, on pourrait envisager la même approche avec un dictionnaire utilisant la clé pour trouver ce qui s'y trouve.