209

Swift に弱参照の配列を保存したいと思います。配列自体は弱参照であってはなりません - その要素は弱参照であるべきです。NSPointerArrayCocoaはこれのタイプセーフでないバージョンを提供していると思います。

4

20 に答える 20

175

次のように汎用ラッパーを作成します。

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 言語ではそのように定義された拡張機能が許可されていないと思います。

于 2014-06-09T20:07:46.207 に答える
75

You can use the NSHashTable with weakObjectsHashTable. NSHashTable<ObjectType>.weakObjectsHashTable()

For Swift 3: NSHashTable<ObjectType>.weakObjects()

NSHashTable Class Reference

Available in OS X v10.5 and later.

Available in iOS 6.0 and later.

于 2014-11-24T15:51:20.737 に答える
15

パーティーにはちょっと遅いですが、私のものを試してみてください。配列ではなくセットとして実装しました。

WeakObjectSet

class WeakObject<T: AnyObject>: Equatable, Hashable {
    weak var object: T?
    init(object: T) {
        self.object = object
    }

    var hashValue: Int {
        if let object = self.object { return unsafeAddressOf(object).hashValue }
        else { return 0 }
    }
}

func == <T> (lhs: WeakObject<T>, rhs: WeakObject<T>) -> Bool {
    return lhs.object === rhs.object
}


class WeakObjectSet<T: AnyObject> {
    var objects: Set<WeakObject<T>>

    init() {
        self.objects = Set<WeakObject<T>>([])
    }

    init(objects: [T]) {
        self.objects = Set<WeakObject<T>>(objects.map { WeakObject(object: $0) })
    }

    var allObjects: [T] {
        return objects.flatMap { $0.object }
    }

    func contains(object: T) -> Bool {
        return self.objects.contains(WeakObject(object: object))
    }

    func addObject(object: T) {
        self.objects.unionInPlace([WeakObject(object: object)])
    }

    func addObjects(objects: [T]) {
        self.objects.unionInPlace(objects.map { WeakObject(object: $0) })
    }
}

使用法

var alice: NSString? = "Alice"
var bob: NSString? = "Bob"
var cathline: NSString? = "Cathline"

var persons = WeakObjectSet<NSString>()
persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObject(bob!)
print(persons.allObjects) // [Bob]

persons.addObjects([alice!, cathline!])
print(persons.allObjects) // [Alice, Cathline, Bob]

alice = nil
print(persons.allObjects) // [Cathline, Bob]

bob = nil
print(persons.allObjects) // [Cathline]

WeakObjectSet は String 型ではなく NSString 型を取ることに注意してください。なぜなら、String 型は AnyType ではないからです。私の迅速なバージョンはApple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29).

コードは Gist から取得できます。 https://gist.github.com/codelynx/30d3c42a833321f17d39

** 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 の動作の違いはこのコンテキストを超えていますが、誰かがこの問題を理解してくれれば幸いです。

于 2016-03-23T16:53:01.573 に答える
12

これは私の解決策ではありません。Apple Developer Forums で見つけました

@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]
于 2014-06-10T08:32:32.700 に答える
10

これを行うには、ウィーク ポインターを保持するラッパー オブジェクトを作成します。

struct WeakThing<T: AnyObject> {
  weak var value: T?
  init (value: T) {
    self.value = value
  }
}

そして、これらを配列で使用します

var weakThings = WeakThing<Foo>[]()
于 2014-06-09T20:09:25.833 に答える
9

機能的なスタイルのラッパーはどうですか?

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 }
于 2014-11-02T08:22:27.313 に答える
8

ジェネリックを使用して弱いコンテナーを作成するという同じ考えがありました。
その結果、次のラッパーを作成しました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)。
そのため、conforming を削除し、アサーションAnyObjectを使用してガードを追加しました。fatalError()

于 2015-11-14T05:44:37.183 に答える
4

WeakContainer の既存の例は役に立ちますが、Lists や Dictionaries などの既存の迅速なコンテナーで弱参照を使用する場合には、実際には役に立ちません。

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

これにより、弱い参照の Dictionary を使用するなど、いくつかのクールなことを行うことができます。

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)
}
于 2015-10-04T21:29:38.977 に答える
2

同じ問題に対するさらに別の解決策...これの焦点は、オブジェクトへの弱い参照を保存することですが、構造体も保存できるようにすることです。

[それがどれほど役立つかはわかりませんが、構文を正しく理解するのに時間がかかりました]

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
于 2015-11-18T22:02:55.880 に答える
2

の周りにラッパーを作成できますArray。または、このライブラリを使用してください https://github.com/NickRybalko/WeakPointerArray let array = WeakPointerArray<AnyObject>() タイプセーフです。

于 2017-07-19T14:03:33.940 に答える
1

他の回答は、ジェネリックの角度をカバーしています。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))
    }
}
于 2015-09-27T03:29:28.800 に答える
0

これは私の解決策です:

  • WeakObjectSet は WeakObject を格納しており、取得していないため、割り当て解除時に配列をクリーンアップします。
  • Set で重複要素が見つかった場合の致命的なエラーを解決します。

--

// 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)) }
    }
}
于 2019-02-06T21:59:59.530 に答える