Разрешение обработчику завершения переживать локальную область, в которой он создан - PullRequest
0 голосов
/ 19 февраля 2019

У меня есть класс, который реализует протокол XMLParserDelegate, и во время инициализации он получает строку и обработчик завершения в качестве аргументов.Я пытаюсь вызвать обработчик завершения после разбора строки, но методы 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(), который не освобождается, даже если он находится внутри какой-то функции.

Ответы [ 2 ]

0 голосов
/ 19 февраля 2019

Вы должны убедиться, что ваш parser объект остается рядом.Прямо сейчас это уходит, как только someFunc заканчивается.Вы можете объявить parser как переменную уровня класса.

0 голосов
/ 19 февраля 2019

Вам нужно хранить MyXMLParser где-то, кроме локальной переменной.Все просто.

Обратите внимание, что UIView.animate(...) - это метод 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)
    }
}

Здесь отдельный анализатор принадлежит его классу, и вы создаете его, вызывая MyXMLParser.parse(myXmlString) { (text) in /* whatever */ }.Важно отметить шаг очистки в parserDidEndDocument, удаляющий экземпляр из хранимой коллекции класса, чтобы у вас не оставалось неиспользуемых экземпляров.

...