Почему нельзя использовать протокол `Encodable` как тип в функции - PullRequest
0 голосов
/ 27 июня 2018

Я пытаюсь получить данные по модели кодирования, которая соответствует протоколу Encodable. Но не удалось вызвать func encode, как показано ниже:

// MARK: - Demo2

class TestClass2: NSObject, Encodable {
    var x = 1
    var y = 2
}


var dataSource2: Encodable?

dataSource2 = TestClass2()

// error: `Cannot invoke 'encode' with an argument list of type '(Encodable)'`
let _ = try JSONEncoder().encode(dataSource2!)
//func encode<T>(_ value: T) throws -> Data where T : Encodable

Но в другом демо все работает хорошо, почему?

// MARK: - Demo1

protocol TestProtocol {
    func test()
}

class TestClass1: NSObject, TestProtocol {
    func test() {
        print("1")
    }

    var x = 1
    var y = 2
}


var dataSource1: TestProtocol?

dataSource1 = TestClass1()


func logItem(_ value: TestProtocol) {
    value.test()
}

logItem(dataSource1!)

Ответы [ 4 ]

0 голосов
/ 29 июня 2019

Существует несколько подходов к решению этой проблемы.

@ SPatel решение расширения Encodable является одной из возможностей. Однако я лично стараюсь не загрязнять предоставляемые Apple протоколы расширениями.

Если я читаю между строк, кажется, что вы хотите передать любую конструкцию , соответствующую Encodable, функции / методу в некоторые другие структура / класс.

Давайте рассмотрим пример того, что я думаю вы пытаетесь достичь:

struct Transform {
    static func toJson(encodable: Encodable) throws -> Data {
        return try JSONEncoder().encode(encodable)
    }
}

Однако Xcode будет жаловаться:

Protocol type 'Encodable' cannot conform to 'Encodable' because only concrete types can conform to protocols

Решение Swift-ier состоит в том, чтобы использовать ограниченный универсальный для функции:

struct Transform {
    static func toJson<EncodableType: Encodable>(encodable: EncodableType) throws -> Data {
        return try JSONEncoder().encode(encodable)
    }
}

Теперь компилятор может вывести тип, соответствующий Encodable, и мы можем вызвать функцию по назначению:

let dataSource = TestClass2()
let jsonData = try? Transform.toJson(encodable: dataSource)
0 голосов
/ 27 июня 2018

Пока TestClass2 равно Encodable, вы можете использовать следующий код. encode должен знать, что кодировать. Это относится к свойствам класса, чтобы сделать это. Encodable сам по себе не содержит никаких свойств.

JSONEncoder().encode(dataSource2 as! TestClass2)
0 голосов
/ 27 июня 2018

Попробуйте этот код, расширяющий кодируемый

extension Encodable {
    func toJSONData() -> Data? {
        return try? JSONEncoder().encode(self)
    }
}

Используйте

var dataSource2: Encodable?
dataSource2 = TestClass2()
let data = dataSource2?.toJSONData()
0 голосов
/ 27 июня 2018

Ваши 2 примера отличаются.

JSONEncoder (). Encode () ожидает конкретный класс, соответствующий прокотолу Encodable. Ссылка dataSource2 содержит протокол, а не конкретный класс.

logItem, с другой стороны, принимает в качестве входных данных только протокол, и НИКАКОЙ конкретный класс, который соответствует протоколу. В этом разница между вашими примерами и тем, почему ваш второй случай работает, а первый - нет.

С вашей текущей настройкой она не будет работать. Вам нужно передать конкретный класс JSONEncoder.

...