0

Eric Armstrongのコードから少し変更したコードがあります

クロージャーをターゲットとして UIButton に追加する

しかし、両方のコードに問題があります。エリックからのものは、上のすべてのターゲットアクションを削除します

func removeTarget(for controlEvent: UIControl.Event = .touchUpInside)

一方、変更されたコードはターゲット アクションをまったく削除しません。もちろんif条件が原因ですが、Storableプロパティにちゃんと格納されている対象がないことも意味します。

extension UIControl: ExtensionPropertyStorable {

    class Property: PropertyProvider {
        static var property = NSMutableDictionary()

        static func makeProperty() -> NSMutableDictionary? {
            return NSMutableDictionary()
        }
    }

    func addTarget(for controlEvent: UIControl.Event = .touchUpInside, target: @escaping (_ sender: Any) ->()) {
        let key = String(describing: controlEvent)
        let target = Target(target: target)

        addTarget(target, action: target.action, for: controlEvent)
        property[key] = target
    }

    func removeTarget(for controlEvent: UIControl.Event = .touchUpInside) {
        let key = String(describing: controlEvent)
        if let target = property[key] as? Target {
            removeTarget(target, action: target.action, for: controlEvent)
            property[key] = nil
        }

    }
}


// Wrapper class for the selector
class Target {

    private let t: (_ sender: Any) -> ()
    init(target t: @escaping (_ sender: Any) -> ()) { self.t = t }
    @objc private func s(_ sender: Any) { t(sender) }

    public var action: Selector {
        return #selector(s(_:))
    }
}

// Protocols with associatedtypes so we can hide the objc_ code
protocol PropertyProvider {
    associatedtype PropertyType: Any

    static var property: PropertyType { get set }
    static func makeProperty() -> PropertyType?
}

extension PropertyProvider {
    static func makeProperty() -> PropertyType? {
        return nil
    }
}

protocol ExtensionPropertyStorable: class {
    associatedtype Property: PropertyProvider
}

// Extension to make the property default and available
extension ExtensionPropertyStorable {

    typealias Storable = Property.PropertyType

    var property: Storable {
        get {
            let key = String(describing: type(of: Storable.self))

            guard let obj = objc_getAssociatedObject(self, key) as? Storable else {

                if let property = Property.makeProperty() {
                    objc_setAssociatedObject(self, key, property, .OBJC_ASSOCIATION_RETAIN)
                }

                return objc_getAssociatedObject(self, key) as? Storable ?? Property.property
            }

            return obj
        }
        set {
            let key = String(describing: type(of: Storable.self))
            return objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_RETAIN) }
    }
}

私の目的は、ターゲット アクションをクロージャーに正確に登録し、#selector を介して特定の UITextField に追加された他のすべてのターゲット アクションを削除せずにそれらを削除することです。これで、クロージャ スタイルのターゲット アクションにこのアプローチを使用しながら、ターゲット アクションのすべてまたはどれも削除できました。

アップデート

Eric Armstrongの回答に基づいて、自分のバージョンを実装しました。しかし、Eric によって提案されたバージョンで私が経験したことは、セルが表示されている間に TableView リストの TextField にターゲット アクションを追加し、セルが消えている間にこのターゲット アクションをテキスト フィールドから削除すると、前のコードは実行時にすべてのターゲット アクションを削除するように見えるということremoveTarget(for:)でした。したがって、UITableViewCell のようなコードの別の場所で、セルが消えて再び画面に表示され、removeTarget(for) が実行されたときに、まったく別のターゲット (このカスタム Target() オブジェクトではなく UITableViewCell オブジェクト) に追加のターゲット アクションを追加した場合、これが実行されます。その他 (ターゲット アクションと呼んでいる外部) も削除され、再度呼び出されることはありませんでした。

値型である [String: Target] ディクショナリの使い方に何らかの問題があったと思いますが、objc_getAssociatedObject のプロパティ getter の場合に使用されていました。

objc_getAssociatedObject(self, key) as? Storable ?? Property.property

したがって、私が理解しているように、特定のキーの objc オブジェクトはなく、Storable は nil であり、nil-coalescing 演算子が呼び出され、静的値型 Property.property return aka [String : Dictionary] がありました

したがって、コピーによって返され、Target オブジェクトはこのコピーされたオブジェクトに格納されました。このコピーされたオブジェクトは、removeTarget(for:) で常に nil として永続的に格納およびアクセスされませんでした。そのため、nil が UIControl.removeTarget() に渡され、すべてのターゲット アクションが常にクリアされました。

[String: Target] Swift 辞書を参照型である NSMutableDictionary に簡単に置き換えてみたので、保存できると思います。しかし、この静的変数の単純な置換と nil-coalesing 演算子を介してそれを返すだけで、Target オブジェクトのストレージが 1 つしかないと仮定したため、Table View をスクロールしているときに、removeForTarget() がすべてのターゲット アクションをすべての UITextFields から何らかの方法で削除したわけではありません。現在からのみ。

また、 String(describing: type(of: Storable.self)) の使用は、特定の Storable タイプに対して常に同じであるため、間違っていると考えています。

4

1 に答える 1