В swift, как мне вернуть объект того же типа, который соответствует протоколу

У меня есть следующий протокол

protocol JSONInitializable {
    static func initialize(fromJSON: NSDictionary) throws -> Self?
}

Теперь я пытаюсь заставить эту функцию возвращать любой класс, соответствующий протоколу. Т.е. если у меня есть класс Foo в соответствии с протоколом, я хочу вернуть Foo объект из метода. Как я могу это сделать?

extension Foo: JSONInitializable {
    static func initialize(fromJSON: NSDictionary) throws -> Foo? {
    }
}

Я получаю ошибку:

Метод 'initialize' в не финальном классе 'Foo' должен возвращать 'Self', чтобы соответствовать протоколу 'JSONInitializable'

2 ответа

Решение

Автозаполнение поможет вам, но в основном, если Foo это класс, вам просто нужен метод, который соответствует точной сигнатуре метода в вашем протоколе:

class Foo {}

protocol JSONInitializable {
    static func initialize(fromJSON: [String : AnyObject]) throws -> Self?
}

extension Foo: JSONInitializable {
    static func initialize(fromJSON: [String : AnyObject]) throws -> Self? {
        return nil
    }
}

Как примечание, вы должны в этом методе заменить все экземпляры Foo с Self, Если вы хотите использовать конструктор, он должен быть помечен как необходимый.

Имея это в виду, вероятно, имеет смысл изменить протокол на использование инициализатора, а не статического метода, называемого initializeВ таком случае взгляните на ответ Блейка Локли. Тем не менее, этот ответ стоит ответить на вопрос о том, как бороться с Self в протоколах, так как, безусловно, есть случаи, когда нам может понадобиться метод, который возвращает Self это не инициализатор.


Если Foo это не класс, это тип значения, который не может быть подклассом, и поэтому мы возвращаем имя типа:

struct Foo: JSONInitializable {
    static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
        return nil
    }
}
enum Foo: JSONInitializable {
    case One, Two, Three

    static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
        return nil
    }
}

Причина, по которой вам нужно вернуться Self? от метода в случае класса из-за наследования. Протокол объявляет, что если вы соответствуете ему, у вас будет метод с именем initialize чей тип возврата будет необязательной версией того, кем вы являетесь.

Если мы напишем Foo"s initialize метод, как вы это написали, то если мы подкласс Foo с Bar, то теперь мы нарушили наше соответствие протоколу. Bar унаследовал соответствие с протоколом от Foo, но у него нет метода, который вызывается initialize и возвращается Bar?, Есть тот, который возвращает Foo,

С помощью Self Здесь означает, что когда наши подклассы наследуют этот метод, он вернет любой тип. Self является эквивалентом Свифта Objective-Cinstancetype,

Как уже упоминалось в ответе nhgrif, измените тип возвращаемого значения Foo? в self?,

альтернативно

В Swift вы можете объявить inits в протоколе, так что попробуйте что-то вроде этого:

protocol JSONInitializable {
    init?(fromJSON: NSDictionary) throws
}

extension Foo: JSONInitializable {
    required init?(fromJSON: NSDictionary) throws {
      //Possibly throws or returns nil
    }
}
Другие вопросы по тегам