Входные переменные типа протокола запрещены?

Следующий код:

protocol SomeProtocol {}
class SomeClass: SomeProtocol {}

private func doSomethingWith(inout someVar: SomeProtocol) {}

private var someGlobalVar = SomeClass() // inferring SomeClass's type

doSomethingWith(&someGlobalVar)

выдает следующую ошибку:

Не удается вызвать «doSomethingWith» со списком аргументов типа «(inout SomeClass)»

Изменение предпоследней строки на private var someGlobalVar: SomeProtocol = SomeClass() устраняет ошибку.

Сабж.


person mesmerizingr    schedule 12.08.2015    source источник
comment
Я удивлен, что у этого вопроса нет больше просмотров! Кажется, что с чем-то еще столкнутся люди... опять же, может быть, люди больше не мутируют объекты с помощью функций с параметрами протокола. :)   -  person Ray Toal    schedule 15.05.2016
comment
@RayToal, возможно, это потому, что переменные inout не так популярны при разработке вашей архитектуры;)   -  person mesmerizingr    schedule 15.05.2016
comment
Верно, и это к лучшему. Протокол только для класса прекрасно решает эту проблему, но это не очевидно.   -  person Ray Toal    schedule 16.05.2016


Ответы (2)


Когда вы присваиваете экземпляр SomeClass переменной при объявлении, предполагается, что тип переменной будет SomeClass. То же, что писать

private var someGlobalVar: SomeClass = SomeClass()

Однако при переходе к параметру inout функция может назначить другой экземпляр этой переменной, например.

private func doSomethingWith(inout someVar: SomeProtocol) {
    someVar = OtherClass()
}

Теперь у вас несоответствие типов. Ошибка, которую вы видите, — это Swift, предотвращающий возникновение аналогичной проблемы.

Другими словами: если вы передаете переменную функции и знаете, что функция может назначить любой экземпляр, принимающий SomeProtocol, этой переменной, вы должны использовать переменную, которая может фактически содержать любой экземпляр, принимающий SomeProtocol:

private var someGlobalVar: SomeProtocol
person Sulthan    schedule 12.08.2015

В дополнение к тому, что сказал @Sulthan, есть еще два возможных решения, в зависимости от того, что должна делать ваша функция.

Вы можете сделать функцию универсальной:

func doSomethingWith<T : SomeProtocol>(inout someVar: T) {}

Теперь вы можете передать экземпляр любого класса, соответствующего протоколу:

var someGlobalVar = SomeClass()
doSomethingWith(&someGlobalVar)

Если вы работаете только с экземплярами класса и функция изменяет только свойства объекта, на который указывает экземпляр, то вам вообще не нужен inout-параметр, поскольку классы являются ссылочными типами. . Вам нужно только пометить протокол как «протокол класса»:

protocol SomeProtocol : class {
    var name : String { get set }
}
class SomeClass: SomeProtocol {
    var name : String = ""
}

func doSomethingWith(someVar: SomeProtocol) {
    // Modify the object:
    someVar.name = "modfied"
}

var someGlobalVar = SomeClass()
doSomethingWith(someGlobalVar)
print(someGlobalVar.name) // "modified"
person Martin R    schedule 12.08.2015