允许完成处理程序在创建它的本地范围内存活
Allowing a completion handler to outlive the local scope where it's created
我有一个 class 实现了 XMLParserDelegate
协议,并且在初始化期间它获取一个字符串和一个完成处理程序作为参数。我试图在解析字符串后调用完成处理程序,但由于 class 的释放,XMLParserDelegate
方法未被访问。
class MyXMLParser: NSObject, XMLParserDelegate {
private (set) var parser: XMLParser?
private (set) var completion: ((String?) -> Void)?
public init(_ xml: String, _ completion: @escaping ((String?) -> Void)) {
let data = xml.data(using: String.Encoding.utf8)
self.parser = XMLParser(data: data ?? Data())
self.completion = completion
self.parser?.delegate = self
self.parser?.parse()
}
deinit {
// Being called before Parser methods
}
// MARK: - Parser delegate methods
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
// Custom implementation
}
func parserDidEndDocument(_ parser: XMLParser) {
// Custom implementation
self.completion?("Test")
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
self.completion?(nil)
}
}
我是这样称呼它的:
func someFunc() {
let parser = MyXMLParser(someXMLString) { text in
// Custom implementation
}
}
我希望闭包在获取值之前保持活动状态,而不是在函数内部的局部作用域结束后取消初始化。我想要实现的一个很好的例子是 UIView.animate()
完成块,即使它位于某个函数内部也不会被释放。
您需要将 MyXMLParser
存储在局部变量以外的其他地方。就这么简单。
注意 UIView.animate(...)
是一个 class 方法。这意味着 class 以某种方式参与了生命周期管理。
一个简单的模仿方法是稍微更改您的界面,这样您就可以让 class 为您完成,而不是直接创建 MyXMLParser
:
class MyXMLParser : NSObject, XMLParserDelegate {
private static var createdParsers: Set<MyXMLParser> = []
static func parse(_ xml: String, _ completion: @escaping (String?) -> Void) {
let newParser = MyXMLParser(xml, completion)
self.createdParsers.insert(newParser)
}
private static func parserDidEndParsing(_ parser: MyXMLParser) {
self.createdParsers.remove(parser)
}
private let parser: XMLParser
private let completion: (String?) -> Void
private init(_ xml: String, _ completion: @escaping (String?) -> Void) {
// Same as existing code
}
//...
func parserDidEndDocument(_ parser: XMLParser) {
//...
MyXMLParser.parserDidEndParsing(self)
}
}
此处,一个单独的解析器由其 class 拥有,您可以通过调用 MyXMLParser.parse(myXmlString) { (text) in /* whatever */ }
创建一个解析器。重要的是要注意 parserDidEndDocument
中的清理步骤,从 class 的存储集合中删除实例,这样您就不会留下未使用的实例。
您需要确保您的 parser
对象保持不变。现在它会在 someFunc
结束后立即消失。您可能希望将 parser
声明为 class 级变量。
我有一个 class 实现了 XMLParserDelegate
协议,并且在初始化期间它获取一个字符串和一个完成处理程序作为参数。我试图在解析字符串后调用完成处理程序,但由于 class 的释放,XMLParserDelegate
方法未被访问。
class MyXMLParser: NSObject, XMLParserDelegate {
private (set) var parser: XMLParser?
private (set) var completion: ((String?) -> Void)?
public init(_ xml: String, _ completion: @escaping ((String?) -> Void)) {
let data = xml.data(using: String.Encoding.utf8)
self.parser = XMLParser(data: data ?? Data())
self.completion = completion
self.parser?.delegate = self
self.parser?.parse()
}
deinit {
// Being called before Parser methods
}
// MARK: - Parser delegate methods
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
// Custom implementation
}
func parserDidEndDocument(_ parser: XMLParser) {
// Custom implementation
self.completion?("Test")
}
func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) {
self.completion?(nil)
}
}
我是这样称呼它的:
func someFunc() {
let parser = MyXMLParser(someXMLString) { text in
// Custom implementation
}
}
我希望闭包在获取值之前保持活动状态,而不是在函数内部的局部作用域结束后取消初始化。我想要实现的一个很好的例子是 UIView.animate()
完成块,即使它位于某个函数内部也不会被释放。
您需要将 MyXMLParser
存储在局部变量以外的其他地方。就这么简单。
注意 UIView.animate(...)
是一个 class 方法。这意味着 class 以某种方式参与了生命周期管理。
一个简单的模仿方法是稍微更改您的界面,这样您就可以让 class 为您完成,而不是直接创建 MyXMLParser
:
class MyXMLParser : NSObject, XMLParserDelegate {
private static var createdParsers: Set<MyXMLParser> = []
static func parse(_ xml: String, _ completion: @escaping (String?) -> Void) {
let newParser = MyXMLParser(xml, completion)
self.createdParsers.insert(newParser)
}
private static func parserDidEndParsing(_ parser: MyXMLParser) {
self.createdParsers.remove(parser)
}
private let parser: XMLParser
private let completion: (String?) -> Void
private init(_ xml: String, _ completion: @escaping (String?) -> Void) {
// Same as existing code
}
//...
func parserDidEndDocument(_ parser: XMLParser) {
//...
MyXMLParser.parserDidEndParsing(self)
}
}
此处,一个单独的解析器由其 class 拥有,您可以通过调用 MyXMLParser.parse(myXmlString) { (text) in /* whatever */ }
创建一个解析器。重要的是要注意 parserDidEndDocument
中的清理步骤,从 class 的存储集合中删除实例,这样您就不会留下未使用的实例。
您需要确保您的 parser
对象保持不变。现在它会在 someFunc
结束后立即消失。您可能希望将 parser
声明为 class 级变量。