弱参照の配列をSwiftに保存したいのですが。配列自体は弱参照であってはなりません-その要素はそうでなければなりません。Cocoa NSPointerArray
はこれのタイプセーフでないバージョンを提供していると思います。
弱参照の配列をSwiftに保存したいのですが。配列自体は弱参照であってはなりません-その要素はそうでなければなりません。Cocoa 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言語がそのように定義された拡張を許可するとは思いません。
NSHashTableをweakObjectsHashTableと共に使用できます。 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を取ることに注意してください。なぜなら、文字列型はAnyTypeではないからです。私の迅速なバージョンはApple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
です。
コードはGistから取得できます。 https://gist.github.com/codelynx/30d3c42a833321f17d39
** 2017年11月に追加
コードを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を。
これは私の解決策ではありません。アップル開発者フォーラムで見つけました。
@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
を使用するにはaでなければなりません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の既存の例は役に立ちますが、リストや辞書などの既存の迅速なコンテナーで弱参照を使用するのには役立ちません。
containsなどの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)
}
ここでGoZonerの偉大な答え@作るに準拠してみましょうHashable
:コンテナのようなオブジェクトにインデックスを作成することができますので、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)) }
}
}
これは、弱いオブジェクトのコンテナを保持するタイプセーフなコレクションです。また、コンテナー/ラッパーがアクセスされると、nilが自動的に削除されます。
例:
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 }
}
}