Swift UITextField目标操作作为结束,存在不删除目标操作的问题

Swift UITextField目标操作作为结束,存在不删除目标操作的问题,swift,uitextfield,swift-protocols,target-action,Swift,Uitextfield,Swift Protocols,Target Action,我对Eric Armstrong的代码做了一些修改 但这两种代码都有问题。来自Eric的操作确实会删除上的所有目标操作 func removeTarget(for controlEvent: UIControl.Event = .touchUpInside) 另一方面,修改后的代码根本不会删除目标操作。当然,这是由if条件引起的,但也意味着在可存储属性中没有正确存储的目标 extension UIControl: ExtensionPropertyStorable { class

我对Eric Armstrong的代码做了一些修改

但这两种代码都有问题。来自Eric的操作确实会删除上的所有目标操作

func removeTarget(for controlEvent: UIControl.Event = .touchUpInside)
另一方面,修改后的代码根本不会删除目标操作。当然,这是由if条件引起的,但也意味着在可存储属性中没有正确存储的目标

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) }
    }
}
我的目标是使用闭包精确地注册目标操作并删除它们,而不删除通过#选择器添加到给定UITextField的所有其他目标操作。现在,我可以在使用这种方法进行闭包式目标操作时删除所有或任何目标操作

更新

根据Eric Armstrong的回答,我已经实现了我的版本。 但我在Eric提出的版本中体验到的是,当单元格出现时,将目标操作添加到TableView列表的TextField,然后在单元格不出现时从文本字段中删除此目标操作时,前面的代码似乎删除了
removeTarget(for:)
exection上的所有目标操作。因此,在代码中的其他位置,如UITableViewCell,我在完全不同的目标(UITableViewCell对象,而不是此自定义目标()对象)上添加了额外的目标操作,同时单元格消失,然后再次出现在屏幕上,并执行removeTarget(for),然后执行此其他(我称之为外部目标操作)也被删除,再也没有打过电话

我认为有些问题是[Strase:Tave]字典的值类型,它是在ObjcxGETActhPosivor对象的属性吸收器中使用的,其中存在

objc_getAssociatedObject(self, key) as? Storable ?? Property.property
所以据我所知,给定的键并没有objc对象,Storable为nil,调用了nil coalescing操作符,静态值类型Property.Property返回aka[String:Dictionary]

所以它是由copy返回的,目标对象存储在这个复制的对象中,而这个复制的对象在removeTarget(for:)中不是永久存储和访问的,始终为nil。因此,nil被传递到UIControl.removetTarget(),并且所有目标操作始终被清除

我尝试过用NSMutableDictionary简单地替换[String:Target]Swift dictionary,NSMutableDictionary是一种引用类型,因此我假设它可以存储。但这个简单的静态变量替换并通过nil coalesing操作符返回它是因为我假设目标对象只有一个这样的存储,然后在滚动表视图时,每个removeForTarget()以某种方式从所有UITextFields中删除了所有目标操作,而不仅仅是从current中


我还考虑使用字符串(描述:类型:(存储:存储))是错误的,因为对于给定的可存储类型,它总是相同的。p> 好吧,我想我终于解决了这个问题

主要问题是使用AssociatedKey!它需要像下面这样做

所以我最终得到了这样的代码:

import UIKit

/**
 * Swift 4.2 for UIControl and UIGestureRecognizer,
 * and and remove targets through swift extension
 * stored property paradigm.
 * https://stackoverflow.com/a/52796515/4415642
 **/

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

        print("ADDED \(ObjectIdentifier(target)), \(target.action)")
    }

    func removeTarget(for controlEvent: UIControl.Event = .touchUpInside) {
        let key = String(describing: controlEvent)

        if let target = property[key] as? Target {
            print("REMOVE \(ObjectIdentifier(target)), \(target.action)")
            removeTarget(target, action: target.action, for: controlEvent)
            property[key] = nil

        }

    }
}

extension UIGestureRecognizer: ExtensionPropertyStorable {

    class Property: PropertyProvider {
        static var property: Target?
    }

    func addTarget(target: @escaping (Any) -> ()) {
        let target = Target(target: target)
        addTarget(target, action: target.action)
        property = target
    }

    func removeTarget() {
        let target = property
        removeTarget(target, action: target?.action)
        property = 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(_:))
    }

    deinit {
        print("Deinit target: \(ObjectIdentifier(self))")
    }
}

// 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 {
            guard let obj = objc_getAssociatedObject(self, &AssociatedKeys.property) as? Storable else {

                if let property = Property.makeProperty() {
                    objc_setAssociatedObject(self, &AssociatedKeys.property, property, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                }

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

            return obj
        }
        set {
            return objc_setAssociatedObject(self, &AssociatedKeys.property, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
    }
}

private struct AssociatedKeys {
    static var property = "AssociatedKeys.property"
}

重要的部分是此&AssociatedKeys.property。它甚至可以在Eric的代码中使用[String:Target]可存储,并且只需在objc_getAssociated之后使用简单的nil合并运算符。。。