Метод в классе 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 в вашей реализации, так как компилятор выведет его из вашего возвращаемого типа.