Swift'te bir dizi zayıf referansı nasıl bildirebilirim?


179

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 NSPointerArraytipsiz bir versiyonunu sunuyor.


1
Başka bir nesneye zayıf bir şekilde başvuran bir konteyner nesnesi yapmaya ve sonra bunlardan bir dizi yapmaya ne dersiniz? (Daha iyi bir cevap
alamazsanız

1
neden bir NSPointerArray kullanmıyorsun?
Bastian

@nielsbot Bu eski bir obj-c çözümü :) Swifty yapmak için genel bir nesne olmalı! :) Ancak gerçek sorun, başvurulan nesne yeniden konumlandırıldığında nesnelerin diziden kaldırılmasıdır.
Sulthan

2
Doğru, parametreli tiplerle bir şey tercih ederim. Sanırım NSPointerArray etrafında parametreli bir sarma yapabilir, ancak herhangi bir alternatif olup olmadığını görmek istedim.
Bill

6
Başka bir seçenek olarak, NSHashTable var. Temel olarak, içerdiği nesnelere nasıl başvurması gerektiğini belirtmenize izin veren bir NSSet'dir.
Mick MacCallum

Yanıtlar:


154

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 Weakya structda 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ı AnyObjectile değiştirilmelidir T- ancak mevcut Swift dilinin bu şekilde tanımlanan bir uzantıya izin verdiğini düşünmüyorum.


11
Sargı nesnelerini değerleri yeniden yerleştirildiğinde diziden nasıl kaldırırsınız?
Sulthan

9
Evet, derleyiciyi çökertti.
GoZoner

5
Lütfen sorun kodunuzu yeni bir soruya gönderin; o zaman hiçbir ding neden cevap Gözat olabilir kodunuzu olun!
GoZoner

2
@EdGamble Sağlanan kod olduğu gibi çalışır, ancak sınıfı Stuffbir protokolle değiştirirseniz başarısız olur ; bu ilgili soruya
Theo

2
Bir yapı, yığın getirme yerine yığın üzerinde tutulacağı için daha iyi olurdu.
KPM

60

NSHashTable'ı poorObjectsHashTable ile kullanabilirsiniz. NSHashTable<ObjectType>.weakObjectsHashTable()

Swift 3 için: NSHashTable<ObjectType>.weakObjects()

NSHashTable Sınıfı Referansı

OS Xv10.5 ve sonraki sürümlerinde kullanılabilir.

İOS 6.0 ve sonraki sürümlerinde kullanılabilir.


En iyi cevap ve sarmalayıcılar için bel zamanı yok!
Ramis

1
Bu zekidir, ancak GoZoner'in cevabı gibi, bu protokoller gibi Anyancak olmayan türlerle çalışmaz AnyObject.
Aaron Brager

@SteveWilford Ama bir sınıf tarafından bir protokol uygulanabilir, bu onu bir referans türü yapar
Aaron Brager

4
bir protokol sınıfı genişletebilir ve daha sonra zayıf olarak kullanabilirsiniz (örn. MyProtocol protokolü: sınıf)
Yasmin Tiomkin

1
Ben bir derleyici hatası alıyorum MyProtocol: classve NSHashTable<MyProtocol>.weakObjects(). "'NSHashTable'," MyProtocol "un bir sınıf türü olmasını gerektiriyor
Greg

14

Parti için biraz geç ama benimkini dene. Dizi değil Set olarak uyguladım.

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) })
    }
}

kullanım

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 NSStringile MyStringbö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, RESOLVEDancak 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.


Projem için bu çözümü benimsedim. İyi iş! Sadece bir öneri, bu çözüm nildeğ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 WeakObjectSeterişildiğinde aradığınızdan emin olduk.
gokeji

Hmm bekleyin, bir nedenden dolayı bu Swift 4 / iOS 11'de çalışmıyor. Görünüşe göre değer nilartık
arttığında

1
Swift4 kodunu güncelledim, cevabın ikinci yarısına bakın. NSString'in bazı anlaşma sorunları var gibi görünüyor, ancak yine de özel sınıf nesneleriniz üzerinde çalışması gerekir.
Kaz Yoshikawa

@KazYoshikawa'ya baktığınız ve cevabı güncellediğiniz için çok teşekkürler! Daha sonra, özel bir sınıfın işe yaradığını, ancak NSStringçalışmadığını fark ettim .
gokeji

2
Tarafından döndürülen işaretçi 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 .
simonseyer

12

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]

1
tuhaf, ama artık yapılarla çalışmıyor. Diyor EXC_BAD_ACCESSbenim için. Sınıf çalışmaları gayet iyi
mente

6
Yapılar değer tipleridir, onlarla çalışmamalıdır. Derleme zamanı hatası olmak yerine çalışma zamanında çökmesi bir derleyici hatasıdır.
David Goodine

10

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>[]()

Vars classkullanmak için bir olmak zorundaweak
Bill

3
Kim söylüyor? Yukarıdaki kod benim için iyi çalışıyor. Tek gereklilik, zayıf hale gelen nesnenin zayıf referansı tutan nesne değil, bir sınıf olması gerektiğidir
Joshua Weinberg

Afedersiniz. "Yapılarda zayıf değişkenler kullanılamaz" diyen bir derleyici mesajı aldım diye yemin edebilirdim. Haklısın - bu derleniyor.
Bill

5
@JoshuaWeinberg Ya Foo bir protokol ise?
onmyway133

@ onmyway133 AFAIK, protokolün yalnızca çalışacağı sınıflar tarafından uygulandığını bildirirse. protocol Protocol : class { ... }
olejnjak

8

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 }

3
Soru, zayıf nesnelerin bir dizisinin nasıl oluşturulacağı (veya Ayarlanacağı).
David H

Bu çözümle, gibi birden çok değere sahip bir dizi bile oluşturabilirsiniz var array: [(x: Int, y: () -> T?)]. Kesinlikle, aradığım şey.
jboi

1
@DavidH Soruyu cevaplamak için cevabımı güncelledim. Umarım bu yardımcı olur.
eonil

Ben sevilen bu yaklaşımı ve bunu süper zeki olduğunu düşünüyorum. Bu stratejiyi kullanarak bir sınıf uygulaması yaptım. Teşekkür ederim!
Ale Ravasio

Hakkında çok emin değilim 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 .
Matic Oblak

7

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ü WeakSetherhangi 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 WeakSetprotokol 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 AnyObjectve fatalError()iddialarla ek korumalar ekledim .


Huh hashtable.allObjects içinde nesne için kullanın
malhal

6

ayrıntılar

  • Swift 5.1, Xcode 11.3.1

Çözüm

struct WeakObject<Object: AnyObject> { weak var object: Object? }

seçenek 1

@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) }
    }
}

Seçenek 1 kullanımı

class Class1 { // or struct
    @WeakElements var weakObjectsArray = [UIView?]() // Use like regular array. With any objects

    func test() {
        weakObjectsArray.append(UIView())
        weakObjectsArray.forEach { print($0) }
    }
}

seçenek 2

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) })
    }
}

Seçenek 2 kullanımı

class Class2 { // or struct
    var weakObjectsArray = WeakObjectsArray<UIView>() // Use like regular array. With any objects

    func test() {
        weakObjectsArray.append(UIView())
        weakObjectsArray.forEach { print($0) }
    }
}

Tam örnek

çö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")
    }
}

Her iki seçenek (ve diğer birçok) ile benim sorunum bu tür dizi protokolleri ile kullanılabilir olmasıdır. Örneğin bu derlenmeyecektir:protocol TP: class { } class TC { var a = WeakArray<TP>() var b = WeakObjectsArray<TP>() }
Matic Oblak

@MaticOblak jenerik ilaç kullanmaya ne dersiniz? protocol TP: class { } class TC<TYPE> where TYPE: TP { var a = WeakObjectsArray<TYPE>() // Use like regular array. With any objects var weakObjectsArray = [TYPE?]() }
Vasily Bodnarchuk

Buradaki fikir, bu dizinin aynı sınıf protokolünü uygulayan farklı türde nesneleri tutabilmesidir. Bir jenerik kullanarak onu tek bir türe kilitlersiniz. Örneğin, böyle bir diziyi tutan bir singletona sahip olduğunu düşünün 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.
Matic Oblak

@MaticOblak ok. Bunu deneyin: 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())
Vasily Bodnarchuk

Şimdi biraz önemli olan dizi ile genel bölümü kaybetti :) Bu sadece yapılabilir değil bir duygu var. Şimdilik Swift'in bir sınırlaması ...
Matic Oblak

4

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)
}

3

İşte GoZoner harika cevap @ yapmak uyması için nasıl Hashable: Konteyner gibi nesneleri dizine eklenebilir, böylece Set, Dictionary, Arrayvb

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
}

3

Beri NSPointerArrayzaten 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 appendve forEachbu yüzden eldeki tam kodu yok).


2

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


1

Diğer cevaplar jenerik açıyı kapsıyor. nilAçı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 nileskilerin 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))
    }
}

& flatMapYerine kullanmaya ne dersiniz ? filtermap
Lukas Kubanek

0

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

0

Bu benim çözümüm:

  • WeakObjectSet depolandığından ve WeakObject'ten çıkmadığından, ayırma sırasında diziyi temizle
  • Küme'de yinelenen öğe bulunduğunda ölümcül hatayı çözme

-

// 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)) }
    }
}

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 }
    }
}

0

İş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.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.