Метод в классе non-final должен возвращать `Self` для соответствия протоколу
При реализации статической функции протокола, возвращающей Self
в расширении протокола, при реализации функции в расширении появляется ошибка (минимальный упрощенный сценарий показан без контекста):
import Foundation
protocol P {
static func f() -> Self
static func g() -> Self
}
extension P {
static func f() -> Self { // Method 'f()' in non-final class 'NSData' must return 'Self' to conform to protocol 'P'
return g()
}
}
extension NSData: P {
static func g() -> Self {
return self.init()
}
}
Замена Self
на P
в строке, в которой произошла ошибка, приводит к тому, что компилятор вызывает ошибку (рис. 11) (что, по-видимому, является допустимым способом передачи ошибки несоответствия типов).
Изменение объявления функции f()
для возврата P
, а также замена Self
на P
в строке ошибки приводит к успешной компиляции, однако теряет точность типа (и требует принудительного даункастинга на каждом сайте вызовов, а также подробно документирует требование Self
),
Существуют ли другие обходные пути для этой проблемы, которые не теряют универсальный тип возвращаемого значения?
РЕДАКТИРОВАТЬ: Дополнительные сведения, чтобы компенсировать отсутствие контекста: P
- это публичный протокол, который будет предоставлен библиотекой для соответствия различных типов (и переопределения g()
), поэтому переопределение f()
в NSData
не вариант. Также предпочтительно не менять f()
на что-то, кроме расширения протокола, так как оно используется библиотекой внутри во многих местах. Учитывая эти две опции, изменение типа возврата f()
на P
является лучшей альтернативой.
Обновить
Начиная с Swift 4 (возможно, 3), приведенный выше код работает как есть.
Ответы
Ответ 1
В Swift 3 или 4:
import Foundation
protocol P {
static func f() -> Self
static func g() -> Self
}
extension P {
static func f() -> Self {
return g()
}
}
extension Data: P {
static func g() -> Data {
return self.init()
}
}
Или вы можете заменить свой класс на последний подкласс:
import Foundation
protocol P {
static func f() -> Self
static func g() -> Self
}
extension P {
static func f() -> Self {
return g()
}
}
import Foundation
final class MyData: NSData {}
extension MyData: P {
static func g() -> Self {
return self.init()
}
}
Если NSData является одним из тех кластеров классов, которые не могут быть легко подклассифицированы (вы увидите stacktrace с __CFRequireConcreteImplementation
), возможно, вам придется создать окончательную оболочку класса для реальных NSData вместо использования подкласса.
Ответ 2
это работает для меня....
protocol P {
static func foo()->Self
}
class C {
required init() {}
}
extension C: P {
static func foo() -> Self {
return self.init()
}
}
let c = C()
let c2 = C.foo()
print(c.dynamicType, c2.dynamicType) // C C
ok, я вижу, что вы заметили, поэтому я сделал обновление
protocol P {
static func foo()->Self
static func bar()->Self
}
extension P {
static func foo() -> Self { // Method 'f()' in non-final class 'NSData' must return `Self` to conform to protocol 'P'
return bar()
}
}
// the class C must be final ...
final class C {
}
// otherwise the compiler can not decide the type of Self in
extension C: P {
static func bar() -> Self {
return self.init()
}
}
let c = C()
let c2 = C.foo()
print(c.dynamicType, c2.dynamicType) // C C
с NSData, если вы хотите сделать то же самое, проблема состоит в том, чтобы объявить NSData окончательным.
Ответ 3
Вам нужно переопределить f() в расширении NSData.
Основная проблема (я думаю), что компилятор не знает, что Self
есть, когда он компилирует f
в расширении протокола, и я думаю, он предполагает, что это должен быть точный тип применяемого класса это тоже. С NSData это может быть не так, потому что у вас может быть подкласс.
Ответ 4
Как и в Swift 2.1, я смог избежать данной ошибки, только имея структуры (или "финальные" классы), соответствующие протоколу. В настоящее время вы можете ограничить реализацию протокола ссылочными типами ( "только для классов" ), но я не видел подобного ограничения для типов значений.
В этом конкретном случае, однако, я бы сказал, что образец делегата подойдет. Если необходимо, вы можете перенести реализацию f по умолчанию в расширение протокола, а реализация подкласса переопределить свойство делегата.
protocol P {
static var delegate : (() -> Self)?;
}
extension P {
static func f() -> Self {
// Use delegate if available, or use default implementation
}
}
extension NSData : P {
static var delegate : (() -> NSData)? = subImplementation;
func subImplementation() -> NSData {
// Some NSData-specific implementation
}
}
Ответ 5
У меня такая же проблема, вы поняли это?
Вот еще один способ, с которым я пришел, чтобы разобраться с конкретной ситуацией.
Вместо использования протокола, для которого требуется статическая функция, возвращающая Self
, возможно, вы можете рассмотреть возможность определения требуемого для инициализатора протокола.
Вот так:
protocol SomeProtocol {
init(someParameter: Int)
}
Не забывайте отмечать реализацию инициализатора ключевым словом required
.
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
}
}
Надеюсь, это поможет.
Ответ 6
Вы также можете обойти это, используя связанный тип.
protocol P {
associatedtype Entity
static func f() -> Entity
static func g() -> Entity
}
extension P {
static func f() -> Entity {
return g()
}
}
extension Data: P {
static func g() -> Data {
return self.init()
}
}
Вам не нужно указывать Entity
в вашей реализации, так как компилятор выведет его из вашего возвращаемого типа.