簡體   English   中英

協議類型不能符合協議,因為只有具體類型才能符合協議

[英]Protocol type cannot conform to protocol because only concrete types can conform to protocols

在應用程序中,我們有兩種類型的貼紙,字符串和位圖。 每個貼紙包可以包含這兩種類型。 這就是我聲明模型的方式:

// Mark: - Models

protocol Sticker: Codable {
}

public struct StickerString: Sticker,  Codable, Equatable {
    let fontName: String
    let character: String
}

public struct StickerBitmap: Sticker,  Codable, Equatable {
    let imageName: String
}

在用戶選擇一些貼紙並使用它們后,我們希望將貼紙保存到UserDefaults以便我們可以向他顯示“最近使用的”貼紙選項卡。 我正在嘗試解碼保存的[Sticker]數組:

let recentStickers = try? JSONDecoder().decode([Sticker].self, from: data)

但我收到以下編譯錯誤:

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

我不明白為什么我將Sticker聲明為Codable ,它也實現了Decodable 任何幫助將不勝感激!

而不是協議使用泛型。

聲明一個簡單的函數

func decodeStickers<T : Decodable>(from data : Data) throws -> T
{
    return try JSONDecoder().decode(T.self, from: data)
}

T可以是單個對象也可以是數組。


在您的結構中刪除Sticker協議。 您還可以刪除Equatable因為它正在結構中合成。

public struct StickerString : Codable {
    let fontName: String
    let character: String
}

public struct StickerBitmap : Codable {
    let imageName: String
}

要解碼其中一種貼紙類型,請對該類型進行注釋

let imageStickers = """
[{"imageName":"Foo"},{"imageName":"Bar"}]
"""    
let stickerData = Data(imageStickers.utf8)

let recentStickers : [StickerBitmap] = try! decodeStickers(from: stickerData)
print(recentStickers.first?.imageName)

let stringSticker = """
{"fontName":"Times","character":"😃"}
"""    
let stickerData = Data(stringSticker.utf8)

let sticker : StickerString = try! decodeStickers(from: stickerData)
print(sticker.character)

要解碼StickerStringStickerBitmap類型的數組, StickerString聲明具有關聯值的包裝器枚舉

enum Sticker: Codable {

    case string(StickerString)
    case image(StickerBitmap)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            let stringData = try container.decode(StickerString.self)
            self = .string(stringData)
        } catch DecodingError.keyNotFound {
            let imageData = try container.decode(StickerBitmap.self)
            self = .image(imageData)
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
            case .string(let string) : try container.encode(string)
            case .image(let image) : try container.encode(image)
        }
    }
}

然后就可以解碼了

let stickers = """
[{"imageName":"Foo"},{"imageName":"Bar"}, {"fontName":"Times","character":"😃"}]
"""

let stickerData = Data(stickers.utf8)
let recentStickers = try! JSONDecoder().decode([Sticker].self, from: stickerData)
print(recentStickers)

在表格視圖中只需switch枚舉

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let sticker = stickers[indexPath.row]
    switch sticker {
    case .string(let stringSticker): 
        let cell = tableView.dequeueReusableCell(withCellIdentifier: "StringStickerCell", for: indexPath) as! StringStickerCell
        // update UI
        return cell
    case .image(let imageSticker): 
        let cell = tableView.dequeueReusableCell(withCellIdentifier: "ImageStickerCell", for: indexPath) as! ImageStickerCell
        // update UI
        return cell
    }
}

這里發生的事情是一種自我解釋


JSONDecoder().decode(/* swift here is expecting class or struct that conforms to Codable */.self, from: data)

但讓我們假設你可以在你的協議中傳遞一個協議

protocol Sticker: Codable {
}

您期望 swift 從數據中解碼的屬性在哪里?

你添加了屬性

public struct StickerString: Sticker,  Codable, Equatable { // it should have redundendant conformance as well as you are conforming to Coddle again
    let fontName: String // here is the properties you are expected to be decoded with the coding keys
    let character: String // here is the properties you are expected to be decoded with the coding keys
}

只要您希望要解碼的類型是動態的,我建議您這樣做

class GenericService< /* here you can pass your class or struct that conforms to Codable */ GenericResponseModel: Codable> {

func buildObjectFromResponse(data: Data?) -> GenericResponseModel? {
        var object : GenericResponseModel?
        do {
            object = try JSONDecoder().decode(GenericResponseModel.self , from: data!)
        } catch (let error){
            print(error)
        }
        return object
    }

}
  • 通過這個類,你可以傳遞符合 Codable 的任何類型甚至列表
  • 然后您將使用以下方法將類型檢查與解碼過程分離
private func handleDecodingTypes (stickers: [Sticker]){
        for sticker in stickers {
            if sticker is StickerString {
                /* do the decoding here */
            }
            if sticker is StickerBitmap {
                /* do the decoding here */
            }
        }
    }

Sticker 協議不是確認/實現 Codable,它實際上是從 Codable 繼承的。 正如錯誤消息所暗示的那樣,協議不符合其他協議,只有具體類型才符合。

protocol Sticker: Codable //This is Protocol inheritance

通過說明

public struct StickerString: Sticker

意味着 Sticker 字符串符合 Sticker 並且 Sticker 是 Codable 的孩子,所以 StickerString 最終符合 Codable。 無需再次聲明一致性,即:

public struct StickerString: Sticker,  Codable //Conformance to Codable is redundant

現在進入解碼部分。

  let recentStickers = try? JSONDecoder().decode([Sticker].self, from: data)

decode 方法需要一個具體的類型。 它沒有關於底層類型或其屬性的任何信息,因為 Sticker 只是一個協議本身,它繼承自 Codable,沒有任何屬性/屬性。 編譯器在解碼后結合 StrickerString 和 StickerBitmap 不會有任何問題,例如

let stickerString = try JSONDecoder().decode(StickerString.self, from: data)
let stickerBitmap = try JSONDecoder().decode(StickerBitmap.self, from: data)
let stickers : [Sticker] = [stickerString, stickerBitmap]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM