Расширение протокола, в котором ассоциированный тип является классом?

Я пробовал что-то вроде этого:

protocol MyModelProtocol {
    var name: String { get set }
}

protocol MyProtocol {
    associatedtype Model: MyModelProtocol

    func changeModel(_ model: Model)
}

extension MyProtocol where Model: AnyObject {
}

Компилятор идет довольный. Однако внутри этого расширения компилятор по-прежнему не уверен, является ли модель классом или структурой. Пример:

extension MyProtocol where Model: AnyObject {
    func changeModel(_ model: Model) {
        model.name = "changed"
    }
}

Таким образом, я получаю сообщение об ошибке: «Невозможно назначить свойство:« модель »является константой« пусть »».

Как я могу сообщить компилятору, что в этом расширении протокола связанный тип всегда будет классом?

Кстати, это всего лишь короткий пример. Я знаю, что в этом случае я мог бы использовать параметр inout, но он не работает для меня, потому что я хочу изменить объект внутри асинхронного обратного вызова следующим образом:

func changeModel(_ model: inout Model, completion: @escaping () -> Void) {
        Api.shared.doRandomAsyncStuff() { (_) in
            model.name = "changed"
            completion()
        }
    }

И попытка сделать это приводит меня к ошибке: «Экранирование замыканий может захватывать входные параметры только явно по значению».


person Bruno Scheltzke    schedule 11.01.2018    source источник
comment
Вам нужно, чтобы протокол также применялся к типам значений (struct, enum)? В противном случае вы можете определить протокол класса: protocol MyModelProtocol: class { .. }   -  person Martin R    schedule 11.01.2018
comment
Да. Раньше у меня это было, но поскольку сейчас я тоже хочу использовать структуры, мне пришлось удалить этот код.   -  person Bruno Scheltzke    schedule 11.01.2018


Ответы (1)


Вы можете просто добавить промежуточное назначение в var. Для типа класса/ссылки это будет иметь тот же эффект, что и установка свойства для исходной ссылки. Для типа структуры будет создана копия, что не сработает, но этого следует избегать из-за ограничения на расширение.

func changeModel(_ model: Model, completion: @escaping () -> Void) {
    var modelRef = model
    Api.shared.doRandomAsyncStuff() { (_) in             
        modelRef.name = "changed"
        completion()
    }
}
person Daniel Hall    schedule 11.01.2018
comment
Это потрясающе! Очень просто! Я только что проверил, и это сработало. Спасибо за вашу помощь. - person Bruno Scheltzke; 11.01.2018