Я хотел бы хранить массив слабых ссылок в Swift. Сам массив не должен быть слабой ссылкой - его элементы должны быть. Я думаю, что Какао NSPointerArray
предлагает нетипичную версию этого.
Я хотел бы хранить массив слабых ссылок в Swift. Сам массив не должен быть слабой ссылкой - его элементы должны быть. Я думаю, что Какао NSPointerArray
предлагает нетипичную версию этого.
Ответы:
Создайте универсальную оболочку как:
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
вы можете использовать либо struct
или class
.
Также, чтобы помочь с получением содержимого массива, вы можете сделать что-то вроде:
extension Array where Element:Weak<AnyObject> {
mutating func reap () {
self = self.filter { nil != $0.value }
}
}
Использование AnyObject
выше должно быть заменено на T
- но я не думаю, что текущий язык Swift допускает расширение, определенное как таковое.
Stuff
протоколом; см. этот связанный вопрос
Вы можете использовать NSHashTable со слабой таблицей. NSHashTable<ObjectType>.weakObjectsHashTable()
Для Swift 3: NSHashTable<ObjectType>.weakObjects()
Доступный в OS X v10.5 и позже.
Доступный в iOS 6.0 и позже.
Any
но не с AnyObject
такими, как протоколы.
MyProtocol: class
и NSHashTable<MyProtocol>.weakObjects()
. «NSHashTable» требует, чтобы «MyProtocol» был типом класса.
Уже поздно для вечеринки, но попробуй мою. Я реализовал как набор, а не массив.
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
Я обновил код до 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
}
}
Затем замените NSString
на MyString
вот так. Тогда странно сказать, что это работает.
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
значения из внутреннего Set
. Поэтому я добавил reap()
функцию, упомянутую в верхнем ответе, и проверял, чтобы она вызывалась при reap()
каждом WeakObjectSet
обращении к ней.
nil
больше
NSString
нет.
UnsafeMutablePointer<T>(&object)
может меняться случайным образом (то же самое с withUnsafePointer
). Теперь я использую версию, поддерживаемую NSHashTable
: gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d .
Это не мое решение. Я нашел это на форумах разработчиков 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]
EXC_BAD_ACCESS
для меня. С классом работает просто отлично
Вы можете сделать это, создав объект-обертку для хранения слабого указателя.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
И затем, используя их в массиве
var weakThings = WeakThing<Foo>[]()
class
чтобы использовать weak
переменные
protocol Protocol : class { ... }
Как насчет функционального стиля оболочки?
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 }
var array: [(x: Int, y: () -> T?)]
. Именно то, что я искал.
let values = Array(array1.map({ $0() })) part
. Поскольку это больше не массив закрытий со слабыми ссылками, значения будут сохраняться до тех пор, пока этот массив не будет освобожден. Если я прав, то важно отметить, что вы никогда не должны сохранять этот массив, self.items = Array(array1.map({ $0() }))
как это соответствует цели.
У меня была такая же идея создать слабый контейнер с генериками.
В результате я создал обертку для 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()
утверждениями.
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) }
}
}
не забудьте вставить код решения
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
. Тогда у вас будет некоторое количество контроллеров представления, которые хотели бы использовать эту функцию. Вы ожидаете позвонить MyManager.delegates.append(self)
. Но если MyManager
привязан к какому-то универсальному типу, тогда это не очень удобно.
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())
Существующий пример 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)
}
Вот как сделать @ большой ответ GoZoner в соответствие Hashable
, так что он может быть проиндексирован в объекты Container , как: Set
, Dictionary
, Array
и т.д.
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
}
Поскольку 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
Это больше работы, но использование в остальной части кода намного чище IMO. Если вы хотите сделать его более похожим на массив, вы можете даже реализовать подписку, сделать это и SequenceType
т. Д. (Но мой проект нуждается только в этом, append
и forEach
поэтому у меня нет точного кода под рукой).
Еще одно решение той же проблемы ... основное внимание уделяется хранению слабой ссылки на объект, но также позволяет хранить структуру.
[Я не уверен, насколько это полезно, но потребовалось время, чтобы понять синтаксис]
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
Вы можете создать обертку вокруг Array
. Или используйте эту библиотеку https://github.com/NickRybalko/WeakPointerArray
let array = WeakPointerArray<AnyObject>()
Это безопасный тип.
Другие ответы охватили общий вид. Думаю, я бы поделился простым кодом, охватывающим этот nil
угол.
Я хотел статический массив (иногда читаемый) из всех, Label
которые в настоящее время существуют в приложении, но не хотел видеть nil
, где были старые.
Ничего особенного, это мой код ...
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
вместо filter
& map
?
Я основал это на работе @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
Это мое решение:
-
// 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)) }
}
}
Это безопасная коллекция типов, которая содержит контейнеры со слабыми объектами. Он также автоматически удаляет ноль контейнеров / упаковщиков при доступе.
Пример:
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 }
}
}
Как насчет функционального подхода ?
let observers = [() -> Observer?]()
observers.append({ [weak anObserver] in return anObserver })
Это основная идея, затем добавьте любую удобную логику, чтобы отслеживать, что находится в массиве. Например, можно рассмотреть тот же подход со словарем, используя ключ как способ найти то, что там.