Разница между T и Self в общих методах - PullRequest
0 голосов
/ 29 марта 2019

Я пишу протокол с именем JSONDataInitializable, который позволит инициализировать значения из Data, который содержит JSON с использованием JSONDecoder.

Поскольку невозможно дляЯвно использовать дженерики в инициализаторах. Я объявил универсальный, независимый от типа вспомогательный метод в расширении протокола, который инициализатор позже вызывает.

Однако я придумал не один, а два способа написания такого метода.

(1):

private static func initialize<T: JSONDataInitializable>(from jsonData: Data) throws -> T {
    return try JSONDecoder().decode(T.self, from: jsonData)
}

(2):

private static func initialize(from jsonData: Data) throws -> Self {
    return try JSONDecoder().decode(Self.self, from: jsonData)
}

Не могли бы вы объяснить разницу между этими методами?Кажется, они оба дают один и тот же результат.

Единственное видимое отличие - это часть соответствия протокола в первом варианте.Однако методы объявлены в расширении протокола и поэтому доступны только для типов, которые соответствуют протоколу.

UPD

Вот полное описание протокола:

protocol JSONDataInitializable: Decodable {
    init?(from jsonData: Data)
}

extension JSONDataInitializable {

    init?(from jsonData: Data) {
        do {
            self = try Self.initialize(from: jsonData)
        } catch {
            print(error)
            return nil
        }
    }

    // (1)
    private static func initialize<T: JSONDataInitializable>(from jsonData: Data) throws -> T {
        return try JSONDecoder().decode(T.self, from: jsonData)
    }

    // ⬆⬆⬆
    // OR
    // ⬇⬇⬇

    // (2)
    private static func initialize(from jsonData: Data) throws -> Self {
        return try JSONDecoder().decode(Self.self, from: jsonData)
    }

}

Допустим, у нас есть структура с именем User, то есть Decodable.Нам нужно инициализировать значения User из JSON (хранится как Data).С протоколом инициализация работает так:

// Protocol conformance declaration
extension User: JSONDataInitializable { }

// JSON stored as Data
let networkData = ...

// Initialization
let john = User(from: networkData)

1 Ответ

1 голос
/ 29 марта 2019

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

С другой стороны, универсальная реализация позволит вам инициализировать любые типы, соответствующие протоколу, но в вашем методе init(from:) вы присваиваете возвращаемое значение self, поэтому параметр универсального типа T будет выводится как Self. Это избавляет от необходимости делать метод универсальным, поскольку для определенного типа T всегда будет Self.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...