Утечка памяти при использовании многоадресного делегата с mvvm-c?

Я не реализовывал шаблон многоадресного делегата ни в одном из своих проектов (до сих пор), поэтому я довольно новичок в этом. Я использую архитектуру MVVM-C и получаю утечку памяти всякий раз, когда пытаюсь вызвать метод делегата из моей модели при изменении данных.

Вот моя реализация многоадресного делегата...

class MulticastDelegate<T> {

// MARK: - Private Properties
private var delegates = [Weak]()

// MARK: - Methods
func add(_ delegate: T) {
    delegates.append(Weak(value: delegate as AnyObject))
}

func remove(_ delegate: T) {
    let weak = Weak(value: delegate as AnyObject)
    if let index = delegates.index(of: weak) {
        delegates.remove(at: index)
    }
}

func invoke(_ invocation: @escaping (T) -> ()) {
    delegates = delegates.filter({$0.value != nil})
    delegates.forEach({
        if let delegate = $0.value as? T {
            invocation(delegate)
        }
    })
} 
}

И слабый...

private class Weak {

// MARK: - Properties
weak var value: AnyObject?

// MARK: - Object Lifecycle
init(value: AnyObject) {
    self.value = value
}
}

// MARK: - Equatable
extension Weak: Equatable {
static func ==(lhs: Weak, rhs: Weak) -> Bool {
    return lhs.value === rhs.value
}
}

А вот и моя модель...

protocol UserModelDelegate: class {
func userDidChange(user: UserData)
}


protocol UserModel {
var delegates: MulticastDelegate<UserModelDelegate> {get}
func fetchUser(completionHandler: @escaping (_ user: UserData?) -> ())
...
}

class MWUserModel: UserModel {

// MARK: - Delegates
var delegates = MulticastDelegate<UserModelDelegate>()

// MARK: - Private Properties
fileprivate var moc: NSManagedObjectContext!

fileprivate var user: UserData? {
    didSet {
    // This is causing the leak...
        delegates.invoke { [weak self] vm in
            if let user = self?.user {
                vm.userDidChange(user: user)
            }
        }
    }
}

// MARK: - Object Lifecycle
init(moc: NSManagedObjectContext) {
    self.moc = moc
}

// MARK: - UserModel
func fetchUser(completionHandler: @escaping (UserData?) -> ()) {
    guard user == nil else {completionHandler(user); return}
    let fetchRequest: NSFetchRequest<NSFetchRequestResult> = User.fetchRequest()
    fetchRequest.fetchLimit = 1

    do {
        let objects = try? moc.fetch(fetchRequest)

        // This triggers didSet on the user property which then invokes the UserModel delegate method 'userDidChange'
        self.user = objects?.first as? UserData
        completionHandler(objects?.first as? UserData)
    }
}

И моя ViewModel...

protocol ProfileViewModelViewDelegate: class {
func userDidChange()
}


protocol ProfileViewModelView {
var model: UserModel? {get set}
var viewDelegate: ProfileViewModelViewDelegate? {get set}
}

class ProfileViewModel: ProfileViewModelView {

// MARK: - Private Properties
fileprivate var user: UserData? {
    didSet {
        viewDelegate?.userDidChange()
    }
}

// MARK: - ProfileViewModelView
weak var viewDelegate: ProfileViewModelViewDelegate?

// This is being injected via the coordinator...
var model: UserModel? {
    didSet {
        model?.delegates.add(self)
        model?.fetchUser(completionHandler: { (user) in
            self.user = user
        })
    }
}
}

// MARK: - UserModelDelegate
extension ProfileViewModel: UserModelDelegate {
func userDidChange(user: UserData) {
     self.user = user
}
}

0 ответов

Другие вопросы по тегам