如何防止对象在当前作为委托时被去初始化

时间:2017-03-06 04:46:29

标签: ios swift uitextfield uialertcontroller uitextfielddelegate

我创建了一个"实用程序类"其唯一目的是UITextFieldDelegate

基本上,只有在文本字段中有文本时才应启用UIAlertAction,否则操作将被禁用。

class ActionDisabler: NSObject, UITextFieldDelegate {

    let action: UIAlertAction

    init(action: UIAlertAction, textField: UITextField) {
        self.action = action
        super.init()
        textField.delegate = self

        // Initialize it as enabled or disabled
        if let text = textField.text {
            action.isEnabled = !text.isEmpty
        } else {
            action.isEnabled = false
        }

    }

    deinit {
        print("Too early?")
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if let text = textField.text {
            action.isEnabled = text.utf8.count - string.utf8.count > 0
        } else {
            action.isEnabled = false
        }

        return true
    }

}

如您所见,它在构造函数中采用警报操作和文本字段,并使自己成为文本字段的委托。简单,对吧?

嗯,这就是定义delegate UITextField的方式:

weak open var delegate: UITextFieldDelegate?

由于这个原因,ActionDisabler在关闭后被解除初始化(文本字段的配置处理程序被添加到UIAlertController,它定义的范围不再在范围内。

alert.addTextField(configurationHandler: { textField in
    _ = ActionDisabler(action: join, textField: textField)
})

this answer所述,对UIAlertController进行子类化并让每个警报控制器成为其自己文本字段的代表都不是一种选择。

有没有办法让ActionDisabler在其作为代表的文本字段不再存在之前不被取消初始化?

1 个答案:

答案 0 :(得分:1)

迄今为止最好的解决方案是保留对ActionDisabler的引用,其中保留对UITextField的引用,因此两者都将同时取消分配。

如果这不可能或者会使您的代码变得丑陋,您可以将UITextField子类化为两次委托。一旦有了强烈的参考:

class UIStrongTextField: UITextField {
    // The delegate will get dealocated when the text field itself gets deallocated
    var strongDelegate: UITextFieldDelegate? {
        didSet {
            self.delegate = strongDelegate
        }
    }
}

现在设置textField.strongDelegate = selfActionDisabler将与文本字段一起取消分配。

如果您无法保留引用,则无法继承UITextField。我们必须要有创意。 ActionDisabler可以保存对自身的引用,并将该引用设置为nil,以释放自身。 (又名手动内存管理)

class ActionDisabler: NSObject, UITextFieldDelegate {

    let action: UIAlertAction
    var deallocPreventionAnchor: ActionDisabler?

    init(action: UIAlertAction, textField: UITextField) {
        self.deallocPreventionAnchor = self
        self.action = action

当你不再需要ActionDisabler时,你设置self.deallocPreventionAnchor = nil并且它将被取消分配。如果您忘记将其设置为nil,则会泄漏内存,因为这确实是通过利用引用计数器进行手动内存管理。而且我已经可以看到人们在评论中尖叫我,因为这有点像hacky :)(这也只有在委托函数触发解除分配时才有意义,否则你无论如何都要在某个地方保留一个引用)