Swift: назначить метод класса обработчику и обработать слабое «я» - PullRequest
0 голосов
/ 05 марта 2020

Я только что обнаружил утечку памяти в приложении, над которым я работаю, и заметил, что следующее вызывает утечку памяти:

class SubClass {

    var didCloseHandler: (() -> Void)?

}

class MainClass {

    var subClass = SubClass()

    func setup {
        subClass.didCloseHandler = self.didCloseSubClass
    }

    func didCloseSubClass() {
        //
    }

}

Это создает цикл сохранения, и по уважительной причине - didCloseHandler сильно захватывает MainClass, а MainClass строго захватывает SubClass.

Мой вопрос: Есть ли способ в Swift, который позволяет мне назначать метод класса обработчику без цикла сохранения?

И да, я знаю, что Я могу сделать это, используя subClass.didCloseHandler = { [weak self] self?.didCloseSubClass() }. Однако мне интересно, можно ли это сделать без введения нового замыкания.

Ответы [ 2 ]

1 голос
/ 05 марта 2020

Если у вас нет сильной ссылки на экземпляр подкласса где-то еще - вы можете попробовать обертку следующим образом:

func WeakPointer<T: AnyObject>(_ object: T, _ method: @escaping (T) -> () -> Void) -> (() -> Void) {
    return { [weak object] in
        method(object!)()
    }
}

Затем используйте ее следующим образом:

func setup() {
    subClass.didCloseHandler = WeakPointer(self, MainClass.didCloseSubClass)
}

Если вы вам не нужны свойства из экземпляра MainClass в реализации didCloseSubClass - вы можете сделать этот метод static, который также решит вашу проблему.

Если у вас есть сильная ссылка на экземпляр SubClass где-то еще, и он выиграл ' не может быть немедленно освобожден - weak var subClass сделает, как уже упоминалось.

РЕДАКТИРОВАТЬ: Я пришел с другой идеей. Это может выглядеть немного сложнее, но, возможно, это поможет.

import Foundation

class SubClass {

    @objc dynamic func didCloseHandler() {
        print(#function)
    }

    deinit {
        print(" \(self) deinit")
    }
}

class MainClass {

    var subClass = SubClass()

    func setup() {
        if let implementation = class_getMethodImplementation(MainClass.self, #selector(didCloseSubClass)),
            let method = class_getInstanceMethod(SubClass.self, #selector(SubClass.didCloseHandler)) {
            method_setImplementation(method, implementation)
        }
    }

    @objc func didCloseSubClass() {
        print(#function)
    }

    deinit {
        print(" \(self) deinit")
    }
}

Вы меняете замыкание для метода @objc dynamic и устанавливаете его реализацию на реализацию из MainClass в setup().

1 голос
/ 05 марта 2020

сделать слабую ссылку на подкласс в MainClass

...