Saya ingin menyimpan sejumlah referensi lemah di Swift. Array itu sendiri seharusnya tidak menjadi referensi yang lemah - elemen-elemennya seharusnya. Saya pikir Cocoa NSPointerArray
menawarkan versi yang tidak aman untuk ini.
Saya ingin menyimpan sejumlah referensi lemah di Swift. Array itu sendiri seharusnya tidak menjadi referensi yang lemah - elemen-elemennya seharusnya. Saya pikir Cocoa NSPointerArray
menawarkan versi yang tidak aman untuk ini.
Jawaban:
Buat pembungkus generik sebagai:
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
Tambahkan instance kelas ini ke array Anda.
class Stuff {}
var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]
Saat mendefinisikan, Weak
Anda dapat menggunakan salah satu struct
atau class
.
Juga, untuk membantu menuai konten array, Anda dapat melakukan sesuatu di sepanjang baris:
extension Array where Element:Weak<AnyObject> {
mutating func reap () {
self = self.filter { nil != $0.value }
}
}
Penggunaan di AnyObject
atas harus diganti dengan T
- tapi saya tidak berpikir bahasa Swift saat ini memungkinkan ekstensi yang ditentukan.
Stuff
dengan protokol; lihat pertanyaan terkait ini
Anda dapat menggunakan NSHashTable dengan lemahObjectsHashTable. NSHashTable<ObjectType>.weakObjectsHashTable()
Untuk Swift 3: NSHashTable<ObjectType>.weakObjects()
Tersedia dalam OS X v10.5 dan yang lebih baru.
Tersedia di iOS 6.0 dan yang lebih baru.
Any
tetapi tidak AnyObject
, seperti protokol.
MyProtocol: class
dan NSHashTable<MyProtocol>.weakObjects()
. "'NSHashTable' mengharuskan 'MyProtocol' menjadi tipe kelas.
Agak terlambat untuk pesta, tapi coba milikku. Saya menerapkan sebagai Set bukan Array.
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]
Berhati-hatilah bahwa WeakObjectSet tidak akan mengambil tipe String selain NSString. Karena, tipe String bukan jenis apa pun. Versi cepat saya adalah Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
.
Kode dapat diambil dari Gist. https://gist.github.com/codelynx/30d3c42a833321f17d39
** DITAMBAH DALAM NOVEMBER.2017
Saya memperbarui kode ke 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) })
}
}
Seperti yang disebutkan gokeji, saya tahu NSString tidak akan dibatalkan alokasi berdasarkan kode yang digunakan. Saya menggaruk kepala dan menulis kelas MyString sebagai berikut.
// 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
}
}
Lalu gantikan NSString
dengan MyString
seperti ini. Kemudian aneh untuk mengatakan itu berhasil.
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]
Kemudian saya menemukan halaman aneh yang mungkin terkait dengan masalah ini.
Referensi yang lemah mempertahankan NSString yang tidak dapat dialokasikan (hanya XC9 + iOS Sim)
https://bugs.swift.org/browse/SR-5511
Dikatakan masalahnya RESOLVED
tetapi saya bertanya-tanya apakah ini masih terkait dengan masalah ini. Bagaimanapun, perbedaan Perilaku antara MyString atau NSString berada di luar konteks ini, tapi saya akan sangat menghargai jika seseorang menemukan masalah ini.
nil
nilai dari internal Set
. Jadi saya telah menambahkan reap()
fungsi yang disebutkan dalam jawaban atas, dan memastikan untuk menelepon reap()
setiap kali WeakObjectSet
diakses.
nil
lagi
NSString
tidak.
UnsafeMutablePointer<T>(&object)
dapat berubah secara acak (sama dengan withUnsafePointer
). Saya sekarang menggunakan versi yang didukung oleh NSHashTable
: gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d .
Ini bukan solusi saya. Saya menemukannya di Forum Pengembang Apple .
@GoZoner memiliki jawaban yang bagus, tetapi itu merusak kompiler Swift.
Berikut ini versi dari wadah objek lemah yang tidak merusak kompiler yang dirilis saat ini.
struct WeakContainer<T where T: AnyObject> {
weak var _value : T?
init (value: T) {
_value = value
}
func get() -> T? {
return _value
}
}
Anda kemudian dapat membuat array dari kontainer ini:
let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
EXC_BAD_ACCESS
untuk saya. Dengan kelas bekerja dengan baik
Anda dapat melakukan ini dengan membuat objek pembungkus untuk memegang pointer lemah.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
Dan kemudian menggunakan ini dalam array
var weakThings = WeakThing<Foo>[]()
class
menggunakan weak
vars
protocol Protocol : class { ... }
Bagaimana dengan pembungkus gaya fungsional?
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)
Panggil saja penutupan kembali untuk memeriksa target masih hidup.
let isAlive = captured1() != nil
let theValue = captured1()!
Dan Anda bisa menyimpan penutupan ini ke dalam array.
let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])
Dan Anda dapat mengambil nilai yang ditangkap dengan lemah dengan memetakan pemetaan penutupan.
let values = Array(array1.map({ $0() }))
Sebenarnya, Anda tidak perlu fungsi untuk membuat penutupan. Hanya menangkap objek secara langsung.
let captured3 = { [weak obj3] in return obj3 }
var array: [(x: Int, y: () -> T?)]
. Persis, apa yang saya cari.
let values = Array(array1.map({ $0() })) part
. Karena ini bukan lagi array penutupan dengan referensi yang lemah, nilai akan dipertahankan hingga array ini tidak dialokasikan. Jika saya benar maka penting untuk dicatat bahwa Anda tidak boleh mempertahankan array ini seperti self.items = Array(array1.map({ $0() }))
ini mengalahkan tujuannya.
Saya memiliki ide yang sama untuk membuat wadah yang lemah dengan obat generik.
Sebagai hasilnya saya membuat pembungkus untuk 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?
}
}
}
Pemakaian:
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()
}
Ini bukan solusi terbaik, karena WeakSet
dapat diinisialisasi dengan jenis apa pun, dan jika jenis ini tidak sesuai dengan AnyObject
protokol maka aplikasi akan macet dengan alasan terperinci. Tapi saya tidak melihat solusi yang lebih baik saat ini.
Solusi asli adalah mendefinisikan WeakSet
dengan cara ini:
class WeakSet<ObjectType: AnyObject>: SequenceType {}
Tetapi dalam kasus WeakSet
ini tidak dapat diinisialisasi dengan protokol:
protocol MyDelegate : AnyObject {
func doWork()
}
let weakSet = WeakSet<MyDelegate>()
Saat ini kode di atas tidak dapat dikompilasi (Swift 2.1, Xcode 7.1).
Itu sebabnya saya turun menyesuaikan diri AnyObject
dan menambahkan penjaga tambahan dengan fatalError()
penegasan.
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) }
}
}
jangan lupa tempel kode solusi
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
. Maka Anda akan memiliki beberapa pengontrol tampilan yang ingin menggunakan fungsi ini. Anda akan menelepon MyManager.delegates.append(self)
. Tetapi jika MyManager
dikunci ke beberapa jenis generik maka ini tidak terlalu berguna.
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())
Contoh WeakContainer yang ada sangat membantu, tetapi itu tidak benar-benar membantu seseorang menggunakan referensi yang lemah dalam wadah cepat yang ada seperti Daftar dan Kamus.
Jika Anda ingin menggunakan metode Daftar seperti berisi, maka WeakContainer perlu mengimplementasikan Equatable. Jadi saya menambahkan kode untuk memungkinkan WeakContainer menjadi setara.
Jika Anda ingin menggunakan WeakContainer dalam kamus, saya juga membuatnya menjadi hashable sehingga dapat digunakan sebagai kunci kamus.
Saya juga menamainya menjadi WeakObject untuk menekankan bahwa ini hanya untuk tipe kelas dan untuk membedakannya dari contoh 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!
}
Ini memungkinkan Anda melakukan beberapa hal keren seperti menggunakan Kamus referensi lemah:
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)
}
Berikut adalah cara untuk membuat @ jawaban yang bagus GoZoner ini sesuai dengan Hashable
, sehingga dapat terindeks di Kontainer benda seperti: Set
, Dictionary
, Array
, dll
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
}
Karena NSPointerArray
sudah menangani sebagian besar dari ini secara otomatis, saya memecahkan masalah dengan membuat pembungkus yang aman untuknya, yang menghindari banyak boilerplate dalam jawaban lain:
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
}
Contoh penggunaan:
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
Ini lebih berfungsi di muka, tetapi penggunaan di sisa kode Anda jauh lebih bersih IMO. Jika Anda ingin membuatnya lebih seperti array, Anda bahkan dapat mengimplementasikan subskript, membuatnya menjadi SequenceType
, dll. (Tetapi proyek saya hanya perlu append
dan forEach
jadi saya tidak memiliki kode yang tepat).
Namun solusi lain untuk masalah yang sama ... fokus yang satu ini adalah menyimpan referensi yang lemah ke suatu objek tetapi memungkinkan Anda untuk menyimpan struct juga.
[Saya tidak yakin seberapa berguna itu, tetapi butuh beberapa saat untuk mendapatkan sintaks yang benar]
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
Anda dapat membuat pembungkus Array
. Atau gunakan pustaka ini https://github.com/NickRybalko/WeakPointerArray
let array = WeakPointerArray<AnyObject>()
Ini adalah tipe aman.
Jawaban lain telah membahas sudut generik. Kupikir aku akan membagikan beberapa kode sederhana yang menutupi nil
sudut.
Saya ingin array statis (baca sesekali) dari semua Label
yang saat ini ada di aplikasi, tetapi tidak ingin melihat nil
di mana yang lama dulu.
Tidak ada yang mewah, ini kode saya ...
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
alih-alih filter
& map
?
Saya mendasarkan ini pada pekerjaan @Eonil, karena saya menyukai strategi penutupan yang lemah, tetapi saya tidak ingin menggunakan operator fungsi untuk suatu variabel, karena rasanya sangat berlawanan dengan intuisi
Apa yang saya lakukan adalah sebagai berikut:
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
}
}
}
Dengan cara ini Anda dapat melakukan sesuatu seperti:
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
Ini solusi saya:
-
// 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)) }
}
}
Ini adalah jenis koleksi aman yang menampung wadah benda lemah. Itu juga otomatis menghapus nil wadah / pembungkus ketika diakses.
Contoh:
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() }
}
}
Koleksi khusus 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 }
}
}
Bagaimana dengan pendekatan fungsional ?
let observers = [() -> Observer?]()
observers.append({ [weak anObserver] in return anObserver })
Ini adalah ide utama, lalu tambahkan logika kenyamanan apa pun untuk melacak apa yang ada dalam array. Misalnya, seseorang dapat mempertimbangkan pendekatan yang sama dengan Kamus menggunakan kunci sebagai cara menemukan apa yang ada di sana.