如何防止对象在当前作为委托时被取消初始化
How to prevent an object from being de-initialized when it is currently being a delegate
我创建了一个 "utility class",其唯一目的是成为一个 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
}
}
如您所见,它在其构造函数中采用警报操作和文本字段,并使自己成为文本字段的委托。简单吧?
嗯,UITextField
的 delegate
是这样定义的:
weak open var delegate: UITextFieldDelegate?
因此,ActionDisabler
在关闭后被取消初始化(添加到 UIAlertController
中定义的文本字段的配置处理程序不再在范围内。
alert.addTextField(configurationHandler: { textField in
_ = ActionDisabler(action: join, textField: textField)
})
子类化 UIAlertController
并让每个警报控制器成为其自己的文本字段的委托不是一种选择,如 this answer 所解释的那样。
有什么方法可以使 ActionDisabler
在作为委托的文本字段不再存在之前不被取消初始化?
迄今为止最好的解决方案是在保留对 UITextField 的引用的地方保留对 ActionDisabler 的引用,这样两者将同时被释放。
如果这不可能或会使您的代码难看,您可以子类化 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 = self
,ActionDisabler
将与文本字段一起释放。
如果您不能持有引用并且不能子类化 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,就会泄漏内存,因为这确实是利用引用计数器进行手动内存管理。而且我已经可以看到人们在评论中对我尖叫,因为这有点老套:)(这也只有在释放将由委托函数触发时才有意义,否则你将不得不在某处保留引用)
我创建了一个 "utility class",其唯一目的是成为一个 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
}
}
如您所见,它在其构造函数中采用警报操作和文本字段,并使自己成为文本字段的委托。简单吧?
嗯,UITextField
的 delegate
是这样定义的:
weak open var delegate: UITextFieldDelegate?
因此,ActionDisabler
在关闭后被取消初始化(添加到 UIAlertController
中定义的文本字段的配置处理程序不再在范围内。
alert.addTextField(configurationHandler: { textField in
_ = ActionDisabler(action: join, textField: textField)
})
子类化 UIAlertController
并让每个警报控制器成为其自己的文本字段的委托不是一种选择,如 this answer 所解释的那样。
有什么方法可以使 ActionDisabler
在作为委托的文本字段不再存在之前不被取消初始化?
迄今为止最好的解决方案是在保留对 UITextField 的引用的地方保留对 ActionDisabler 的引用,这样两者将同时被释放。
如果这不可能或会使您的代码难看,您可以子类化 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 = self
,ActionDisabler
将与文本字段一起释放。
如果您不能持有引用并且不能子类化 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,就会泄漏内存,因为这确实是利用引用计数器进行手动内存管理。而且我已经可以看到人们在评论中对我尖叫,因为这有点老套:)(这也只有在释放将由委托函数触发时才有意义,否则你将不得不在某处保留引用)