Swift'te bir dizi zayıf referansı saklamak istiyorum. Dizinin kendisi zayıf bir referans olmamalıdır - elemanları olmalıdır. Bence Cocoa bunun NSPointerArray
tipsiz bir versiyonunu sunuyor.
Swift'te bir dizi zayıf referansı saklamak istiyorum. Dizinin kendisi zayıf bir referans olmamalıdır - elemanları olmalıdır. Bence Cocoa bunun NSPointerArray
tipsiz bir versiyonunu sunuyor.
Yanıtlar:
Aşağıdaki gibi genel bir sarmalayıcı oluşturun:
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
Dizinize bu sınıfın örneklerini ekleyin.
class Stuff {}
var weakly : [Weak<Stuff>] = [Weak(value: Stuff()), Weak(value: Stuff())]
Tanımlarken Weak
ya struct
da kullanabilirsiniz class
.
Ayrıca, dizi içeriğini toplama konusunda yardımcı olmak için, aşağıdaki satırlarda bir şeyler yapabilirsiniz:
extension Array where Element:Weak<AnyObject> {
mutating func reap () {
self = self.filter { nil != $0.value }
}
}
Yukarıdakilerin kullanımı AnyObject
ile değiştirilmelidir T
- ancak mevcut Swift dilinin bu şekilde tanımlanan bir uzantıya izin verdiğini düşünmüyorum.
Stuff
bir protokolle değiştirirseniz başarısız olur ; bu ilgili soruya
NSHashTable'ı poorObjectsHashTable ile kullanabilirsiniz. NSHashTable<ObjectType>.weakObjectsHashTable()
Swift 3 için: NSHashTable<ObjectType>.weakObjects()
OS Xv10.5 ve sonraki sürümlerinde kullanılabilir.
İOS 6.0 ve sonraki sürümlerinde kullanılabilir.
Any
ancak olmayan türlerle çalışmaz AnyObject
.
MyProtocol: class
ve NSHashTable<MyProtocol>.weakObjects()
. "'NSHashTable'," MyProtocol "un bir sınıf türü olmasını gerektiriyor
Parti için biraz geç ama benimkini dene. Dizi değil Set olarak uyguladım.
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'in String türünü değil NSString'i almaya dikkat edin. Çünkü Dize türü AnyType değildir. Hızlı versiyonumApple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
.
Kod Gist'ten alınabilir. https://gist.github.com/codelynx/30d3c42a833321f17d39
** KASIM.2017'DE EKLENDİ
Kodu Swift 4 olarak güncelledim
// 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'nin de belirttiği gibi, NSString'in kullanımdaki koda göre dağıtılmayacağını anladım. Başımı kaşıdım ve MyString sınıfını şöyle yazdım.
// 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
}
}
Sonra değiştirmek NSString
ile MyString
böyle. Sonra işe yaradığını söylemek garip.
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]
Sonra garip bir sayfa bu sorunla ilgili olabilir bulundu.
Zayıf referans, ayrılmış NSString'i korur (yalnızca XC9 + iOS Sim)
https://bugs.swift.org/browse/SR-5511
Sorun olduğunu söylüyor, RESOLVED
ancak bunun hala bu sorunla ilgili olup olmadığını merak ediyorum. Her neyse, MyString veya NSString arasındaki davranış farklılıkları bu bağlamın ötesinde, ancak birisi bu sorunu çözerse takdir ediyorum.
nil
değerleri içten kaldırmıyor gibi görünüyor Set
. Bu yüzden reap()
en üstteki cevaba değinilen bir fonksiyon ekledim ve reap()
her WeakObjectSet
erişildiğinde aradığınızdan emin olduk.
nil
artık
NSString
çalışmadığını fark ettim .
UnsafeMutablePointer<T>(&object)
rasgele (aynı ile withUnsafePointer
) değişebilir deneyim yaptım . Şimdi bir tarafından desteklenen bir sürümünü kullanın NSHashTable
: gist.github.com/simonseyer/cf73e733355501405982042f760d2a7d .
Bu benim çözümüm değil. Apple Geliştirici Forumlarında buldum .
@GoZoner'ın iyi bir cevabı var, ancak Swift derleyicisini çökertiyor.
İşte zayıf nesne kapsayıcısının bir sürümü şu anda yayınlanan derleyiciyi çökertmiyor.
struct WeakContainer<T where T: AnyObject> {
weak var _value : T?
init (value: T) {
_value = value
}
func get() -> T? {
return _value
}
}
Daha sonra şu kapsayıcılardan oluşan bir dizi oluşturabilirsiniz:
let myArray: Array<WeakContainer<MyClass>> = [myObject1, myObject2]
EXC_BAD_ACCESS
benim için. Sınıf çalışmaları gayet iyi
Bunu, zayıf bir işaretçiyi tutmak için bir sarıcı nesne oluşturarak yapabilirsiniz.
struct WeakThing<T: AnyObject> {
weak var value: T?
init (value: T) {
self.value = value
}
}
Ve sonra bunları dizide kullanmak
var weakThings = WeakThing<Foo>[]()
class
kullanmak için bir olmak zorundaweak
protocol Protocol : class { ... }
Fonksiyonel tarzı sarıcı hakkında nasıl?
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)
Hedefin hala hayatta olup olmadığını kontrol etmek için iade kapısını aramanız yeterlidir.
let isAlive = captured1() != nil
let theValue = captured1()!
Ve bu kapanışları bir dizi halinde saklayabilirsiniz.
let array1 = Array<() -> (Class1?)>([captured1, captured2, captured3])
Ve kapanışları çağırarak eşleştirerek zayıf yakalanan değerleri alabilirsiniz.
let values = Array(array1.map({ $0() }))
Aslında, kapanış yapmak için bir işleve ihtiyacınız yok. Sadece bir nesneyi doğrudan yakalayın.
let captured3 = { [weak obj3] in return obj3 }
var array: [(x: Int, y: () -> T?)]
. Kesinlikle, aradığım şey.
let values = Array(array1.map({ $0() })) part
. Bu artık zayıf referanslara sahip bir kapanış dizisi olmadığından, bu dizi yeniden konumlandırılana kadar değerler korunacaktır. Eğer doğruysam self.items = Array(array1.map({ $0() }))
, bu amacı asla attığı gibi asla bu diziyi tutmamanız gerektiğini belirtmek önemlidir .
Jeneriklerle zayıf bir kap oluşturmak için aynı fikrim vardı.
Sonuç olarak ben için sarıcı oluşturdu 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?
}
}
}
Kullanımı:
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()
}
Bu en iyi çözüm değildir, çünkü WeakSet
herhangi bir türle başlatılabilir ve bu tür uygun değilseAnyObject
protokole uygulama ayrıntılı bir nedenden dolayı çökecektir. Ama şu anda daha iyi bir çözüm göremiyorum.
Orijinal çözüm şu şekilde tanımlamaktı WeakSet
:
class WeakSet<ObjectType: AnyObject>: SequenceType {}
Ancak bu durumda WeakSet
protokol ile başlatılamaz:
protocol MyDelegate : AnyObject {
func doWork()
}
let weakSet = WeakSet<MyDelegate>()
Şu anda yukarıdaki kod derlenemez (Swift 2.1, Xcode 7.1).
Bu yüzden uygunluklarını bıraktım AnyObject
ve fatalError()
iddialarla ek korumalar ekledim .
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) }
}
}
çözüm kodunu yapıştırmayı unutmayın
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
. O zaman bu işlevselliği kullanmak isteyen bazı görünüm denetleyicilerine sahip olursunuz. Aramayı beklersiniz MyManager.delegates.append(self)
. Ancak MyManager
, bazı genel türlere kilitlenmişse, bu çok kullanışlı değildir.
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())
Mevcut WeakContainer örneği yararlıdır, ancak Listeler ve Sözlükler gibi mevcut hızlı kaplarda zayıf referansların kullanılmasına gerçekten yardımcı olmaz.
İçerir gibi Liste yöntemlerini kullanmak istiyorsanız, WeakContainer uygulamasının Equatable uygulamasını kullanması gerekir. Bu yüzden WeakContainer eşit olabilir izin kodu ekledi.
Sözlüklerde WeakContainer'ı kullanmak istediğinizde, sözlüğü de kullanılabilir hale getirdim, böylece sözlük anahtarları olarak kullanılabilir.
Ben de sadece sınıf türleri için olduğunu vurgulamak ve WeakContainer örneklerinden ayırt etmek için WeakObject için yeniden adlandırdı:
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!
}
Bu, zayıf referansların Sözlüğünü kullanmak gibi bazı harika şeyler yapmanızı sağlar:
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)
}
İşte GoZoner harika cevap @ yapmak uyması için nasıl Hashable
: Konteyner gibi nesneleri dizine eklenebilir, böylece Set
, Dictionary
, Array
vb
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
}
Beri NSPointerArray
zaten otomatik olarak bunun en kolları, ben diğer yanıtlar Demirbaş bir sürü engeller bunun için bir tip-güvenli sarmalayıcı, yaparak sorunu çözdü:
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
}
Örnek kullanım:
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
Ön tarafta daha fazla çalışma var, ancak kodunuzun geri kalanında kullanım çok daha temiz IMO. Daha dizi benzeri yapmak istiyorsanız, hatta abonelik uygulayabilir SequenceType
, bir vb. Yapabilirsiniz (ancak projemin sadece ihtiyacı var append
ve forEach
bu yüzden eldeki tam kodu yok).
Yine aynı soruna başka bir çözüm ... bunun odak noktası, bir nesneye zayıf bir referans depolamak, ancak bir yapıyı da saklamanıza izin vermektir.
[Ne kadar yararlı olduğundan emin değilim, ancak sözdizimini doğru yapmak biraz zaman aldı]
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
Etrafında sarıcı oluşturabilirsin Array
. Veya bu kütüphaneyi kullanın https://github.com/NickRybalko/WeakPointerArray
let array = WeakPointerArray<AnyObject>()
Bu tür bir kasadır.
Diğer cevaplar jenerik açıyı kapsıyor. nil
Açıyı kapsayan basit bir kod paylaşacağımı düşündüm .
Label
Şu anda uygulamada var olan tüm s statik bir dizi (zaman zaman okumak) istedim , ama nil
eskilerin olduğu yerde görmek istemiyordu .
Hiçbir şey fantezi, bu benim kodum ...
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
Yerine kullanmaya ne dersiniz ? filter
map
Bunu @Eonil'in çalışmasına dayandırdım, çünkü kapanma zayıf bağlama stratejisini sevdim, ancak bir değişken için bir işlev operatörü kullanmak istemedim, çünkü aşırı derecede sezgisel hissetti.
Bunun yerine yaptığım şudur:
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
}
}
}
Bu şekilde aşağıdakileri yapabilirsiniz:
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
Bu benim çözümüm:
-
// 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)) }
}
}
Bu, zayıf nesnelerin kaplarını tutan güvenli bir koleksiyondur. Ayrıca, erişildiğinde kapları / ambalajları otomatik olarak kaldırır.
Misal:
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() }
}
}
Özel koleksiyon 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 }
}
}
İşlevsel bir yaklaşım ne olacak ?
let observers = [() -> Observer?]()
observers.append({ [weak anObserver] in return anObserver })
Bu ana fikirdir, sonra dizide ne olduğunu takip etmek için herhangi bir kolaylık mantığı ekleyin. Örneğin, bir anahtarla, içinde ne olduğunu bulmak için bir anahtar kullanarak aynı yaklaşımı düşünebilirsiniz.