Как реализуешь коллекцию которая хранит Weak ссылки на объекты?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Реализация коллекции со слабыми ссылками в iOS
Для реализации коллекции, хранящей weak ссылки на объекты в iOS/Swift, необходимо учитывать особенности управления памятью и то, что стандартные коллекции (Array, Set, Dictionary) хранят strong references. Я рассмотрю несколько подходов, начиная с наиболее распространённого.
Ключевая концепция: NSHashTable
Основной способ — использование NSHashTable из Foundation, который напрямую поддерживает weak хранение:
import Foundation
class WeakObjectCollection<T: AnyObject> {
private weak var table = NSHashTable<T>.weakObjects()
var allObjects: [T] {
return table.allObjects.compactMap { $0 }
}
func add(_ object: T) {
table.add(object)
}
func remove(_ object: T) {
table.remove(object)
}
func contains(_ object: T) -> Bool {
return table.contains(object)
}
func removeAll() {
table.removeAllObjects()
}
}
// Пример использования
class MyClass {
let name: String
init(name: String) { self.name = name }
}
let collection = WeakObjectCollection<MyClass>()
var instance: MyClass? = MyClass(name: "Test")
collection.add(instance!)
print(collection.allObjects.count) // 1
instance = nil // Объект должен быть освобождён
print(collection.allObjects.count) // 0 (автоматически очищено)
Альтернативная реализация через массив weak ссылок
Можно создать обёртку для массива, используя weak свойства внутри структуры-контейнера:
class WeakBox<T: AnyObject> {
weak var value: T?
init(_ value: T) {
self.value = value
}
}
struct WeakArray<T: AnyObject> {
private var boxes: [WeakBox<T>] = []
var allObjects: [T] {
// Автоматическая очистка nil значений при обращении
boxes = boxes.filter { $0.value != nil }
return boxes.compactMap { $0.value }
}
mutating func append(_ object: T) {
boxes.append(WeakBox(object))
}
mutating func remove(_ object: T) {
boxes.removeAll { $0.value === object || $0.value == nil }
}
mutating func compact() {
boxes = boxes.filter { $0.value != nil }
}
}
Реализация weak словаря через NSMapTable
Для пар "ключ-значение" следует использовать NSMapTable:
class WeakDictionary<Key: AnyObject & Hashable, Value: AnyObject> {
private let mapTable = NSMapTable<Key, Value>(
keyOptions: [.strongMemory],
valueOptions: [.weakMemory]
)
var allKeys: [Key] {
return mapTable.keyEnumerator().allObjects as? [Key] ?? []
}
var allValues: [Value] {
return mapTable.objectEnumerator()?.allObjects.compactMap { $0 as? Value } ?? []
}
subscript(key: Key) -> Value? {
get { return mapTable.object(forKey: key) }
set {
if let newValue = newValue {
mapTable.setObject(newValue, forKey: key)
} else {
mapTable.removeObject(forKey: key)
}
}
}
}
Важные аспекты реализации
-
Автоматическая очистка: NSHashTable и NSMapTable автоматически удаляют nil ссылки, но не мгновенно. В кастомных реализациях нужно предусмотреть метод
compact(). -
Потокобезопасность: Коллекции по умолчанию не потокобезопасны. Для многопоточного доступа нужны механизмы синхронизации:
class ThreadSafeWeakCollection<T: AnyObject> {
private var table = NSHashTable<T>.weakObjects()
private let queue = DispatchQueue(label: "com.weakcollection.queue",
attributes: .concurrent)
func add(_ object: T) {
queue.async(flags: .barrier) {
self.table.add(object)
}
}
var allObjects: [T] {
queue.sync {
return self.table.allObjects.compactMap { $0 }
}
}
}
-
Особенности Swift: В чистом Swift нет встроенных weak коллекций, поэтому используем Foundation (NSHashTable/NSMapTable) или создаём обёртки.
-
Использование enums для более безопасного дизайна:
enum WeakCollection<T: AnyObject> {
case hashTable(NSHashTable<T>)
case customArray([WeakBox<T>])
mutating func add(_ object: T) {
switch self {
case .hashTable(let table):
table.add(object)
case .customArray(var array):
array.append(WeakBox(object))
self = .customArray(array)
}
}
}
Практические рекомендации
- Для большинства случаев используйте NSHashTable — это наиболее оптимизированное и надёжное решение
- Реализуйте протокол Sequence/Collection для удобства использования в for-in циклах
- Учитывайте производительность при частых операциях очистки
- Тестируйте утечки памяти с помощью Instruments (Leaks tool)
- Для observer-паттернов weak коллекции идеальны для хранения подписчиков
Выбор конкретной реализации зависит от требований: NSHashTable оптимален для производительности, кастомные решения дают больше гибкости, но требуют внимательной реализации очистки мёртвых ссылок.