Я хочу сохранить массив слабых ссылок в Swift. Сам массив не должен быть слабой ссылкой - его элементы должны быть. Я думаю, что Cocoa NSPointerArray
предлагает нестандартную версию этого.
Как объявить массив слабых ссылок в Swift?
Ответ 1
Создайте универсальную оболочку как:
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 допускает расширение, определенное как таковое.
Ответ 2
Вы можете использовать NSHashTable со значением weakObjectsHashTable. NSHashTable.weakObjectsHashTable()
Для Swift 3: NSHashTable.weakObjects()
Доступно в OS X v10.5 и более поздних версиях.
Доступно в iOS 6.0 и более поздних версиях.
Ответ 3
Это не мое решение. Я нашел его на форумах разработчиков 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]
Ответ 4
Это вроде поздно для вечеринки, но попробуйте мой. Я реализовал как набор не массив.
WeakObjectSet
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
** ДОБАВЛЕН В NOV.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 выходят за рамки этого контекста, но я был бы признателен, если бы кто-то понял эту проблему.
Ответ 5
Вы можете сделать это, создав объект-оболочку, чтобы удерживать слабый указатель.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
И затем используя их в массиве
var weakThings = WeakThing<Foo>[]()
Ответ 6
Как насчет оболочки функционального стиля?
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() }))
Ответ 7
У меня была та же идея создать слабый контейнер с дженериками.
В результате я создал обертку для 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()
.
Ответ 8
Существующий пример WeakContainer полезен, но на самом деле это не помогает использовать слабые ссылки в существующих быстрых контейнерах, таких как списки и словари.
Если вы хотите использовать методы List, такие как содержит, то 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)
}
Ответ 9
На основании [Kaz Yoshikawa] [1] ответа
подробности
- Xcode 10.2 (10E125), Swift 5
Решение
import Foundation
protocol Weakable: class {
associatedtype T: AnyObject = Self
var asWeakValue: WeakObject<T> { get }
}
protocol WeakObjectProtocol {
associatedtype WeakObjectType
var value: WeakObjectType? {get set}
init(object: WeakObjectType)
}
class WeakObject<T: AnyObject>: WeakObjectProtocol {
typealias WeakObjectType = T
weak var value: WeakObjectType?
required init(object: WeakObjectType) {
self.value = object
}
var referenceCount: Int {
return CFGetRetainCount(value)
}
}
extension WeakObject: Equatable {
static func == (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
return lhs.value === rhs.value
}
}
extension WeakObject: CustomStringConvertible {
var description: String {
if let value = value {
let className = String(describing: type(of: value.self))
return "{class: \(className); referenceCount: \(referenceCount)}"
}
return "nil"
}
}
extension Array where Element: AnyObject {
var asWeakArray: Array<WeakObject<Element>> {
var weakArray = [WeakObject<Element>]()
for item in self {
let obj = WeakObject(object: item)
weakArray.append(obj)
}
return weakArray
}
}
использование
extension UIView: Weakable {
var asWeakValue: WeakObject<UIView> { return WeakObject(object: self) }
}
var weakArray = [WeakObject<UIView>]()
weakArray = view.subviews.asWeakArray
weakArray.append(view.asWeakValue)
Полный образец
не забудьте вставить код решения
import UIKit
class ViewController: UIViewController {
private var weakArray = [WeakObject<UIView>]()
override func viewDidLoad() {
super.viewDidLoad()
addSubviews()
weakArray = view.subviews.asWeakArray
weakArray.append(createRandomRectangleAndAdd(to: view).asWeakValue)
}
private func printArray(title: String) {
print("=============================\n\(title)\ncount: \(weakArray.count)")
weakArray.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() {
_ = createRandomRectangleAndAdd(to: view)
_ = createRandomRectangleAndAdd(to: view)
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
.filter { view -> Bool in return !(view is UIButton) }
.first?.removeFromSuperview()
view.layoutIfNeeded()
printArray(title: "First view deleted")
}
@objc func addView() {
weakArray.append(createRandomRectangleAndAdd(to: view).asWeakValue)
printArray(title: "View addded")
}
@objc func removeNils() {
weakArray = weakArray.filter { $0.value != nil }
printArray(title: "Remove all nil elements in weakArray")
}
}
extension UIView: Weakable {
var asWeakValue: WeakObject<UIView> { return WeakObject(object: self) }
}
Ответ 10
Здесь, как сделать отличный ответ @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
}
Ответ 11
Другие ответы охватывают угол генериков. Думаю, я бы разделил простой код, охватывающий угол 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))
}
}
Ответ 12
Еще одно решение одной и той же проблемы... основное внимание в этом вопросе заключается в сохранении слабой ссылки на объект, но позволяющей также хранить структуру.
[Я не уверен, насколько это полезно, но для получения синтаксиса это заняло некоторое время]
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
Ответ 13
Вы можете создать обертку вокруг Array
. Или используйте эту библиотеку https://github.com/NickRybalko/WeakPointerArray
let array = WeakPointerArray<AnyObject>()
Это безопасный тип.
Ответ 14
Поскольку 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
поэтому у меня нет точного кода под рукой).
Ответ 15
Я основал это на работе @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
Ответ 16
Это мое решение:
- Очистите массив при освобождении, потому что WeakObjectSet хранит и не выходит из WeakObject
- Устраните фатальную ошибку при обнаружении дубликата элемента в наборе
-
// 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)) }
}
}
Ответ 17
Это безопасная коллекция типов, которая содержит контейнеры со слабыми объектами. Он также автоматически удаляет ноль контейнеров/упаковщиков при доступе.
Пример:
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 }
}
}