[英]Adding property observer to Decodable protocol in Swift 4
我正在從 JSON 解析中檢索 URL,然后我想將該 URL 轉換為 UIImage。 這是我當前的數據結構:
struct Book: Decodable {
let author: String
let artworkURL: URL
let genres: [Genre]
let name: String
let releaseDate: String
}
我試圖做:
struct Book: Decodable {
let author: String
var bookArtwork: UIImage
let artworkURL: URL {
didSet {
do {
if let imageData: Data = try Data(contentsOf: artworkURL) {
bookArtwork = UIImage(data: imageData)!
}
} catch {
print(error)
}
}
let genres: [Genre]
let name: String
let releaseDate: String
}
但這does not conform to protocol 'Decodable'
還有其他人有解決方案嗎?
下面我將列舉一些處理這種情況的方法,但正確的答案是您不應該在解析 JSON 期間檢索圖像。 而且您絕對不應該同步執行此操作(這就是Data(contentsOf:)
所做的)。
相反,您應該只檢索 UI 需要的圖像。 並且您希望將圖像檢索到可以在低內存情況下清除的內容,以響應.UIApplicationDidReceiveMemoryWarning
系統通知。 最重要的是,如果您不小心,並且您幾乎總是想將圖像與模型對象本身分離,圖像可能會占用驚人的內存量。
但是,如果您絕對決定將圖像合並到Book
對象中(我再次建議不要這樣做),您可以:
您可以將bookArtwork
lazy
變量:
struct Book: Decodable { let author: String lazy var bookArtwork: UIImage = { let data = try! Data(contentsOf: artworkURL) return UIImage(data: data)! }() let artworkURL: URL let genres: [Genre] let name: String let releaseDate: String }
在這種情況下,這是一種可怕的模式(同樣,因為我們永遠不應該進行同步網絡調用),但它說明了lazy
變量的想法。 您有時也會使用計算屬性來執行這種模式。
同樣糟糕(出於同樣的原因,同步網絡調用是邪惡的),您還可以實現自定義init
方法:
struct Book: Decodable { let author: String let bookArtwork: UIImage let artworkURL: URL let genres: [Genre] let name: String let releaseDate: String init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) author = try values.decode(String.self, forKey: .author) artworkURL = try values.decode(URL.self, forKey: .artworkURL) genres = try values.decode([Genre].self, forKey: .genres) name = try values.decode(String.self, forKey: .name) releaseDate = try values.decode(String.self, forKey: .releaseDate) // special processing for artworkURL let data = try Data(contentsOf: artworkURL) bookArtwork = UIImage(data: data)! } enum CodingKeys: String, CodingKey { case author, artworkURL, genres, name, releaseDate } }
事實上,這比之前的模式更糟糕,因為至少在第一個選項中,同步網絡調用被推遲到您引用UIImage
屬性時。
但有關此通用模式的更多信息,請參閱編碼和解碼自定義類型中的手動編碼和解碼。
我提供了上面這兩個模式,作為示例,說明如何擁有一些不是 JSON 的一部分,而是以其他方式初始化的屬性。 但是因為您使用的是Data(contentsOf:)
,所以這些都不是真正可取的。 但是如果有問題的屬性不需要一些耗時的同步任務,就像你在這里做的那樣,它們是很好的模式,需要注意。
在這種情況下,我認為最簡單的方法是提供一種在需要時異步檢索圖像的方法:
完全消除同步網絡調用,只提供一個異步方法來檢索圖像:
struct Book: Decodable { let author: String let artworkURL: URL let genres: [Genre] let name: String let releaseDate: String func retrieveImage(completionHandler: @escaping (UIImage?, Error?) -> Void) { let task = URLSession.shared.dataTask(with: artworkURL) { data, _, error in guard let data = data, error == nil else { completionHandler(nil, error) return } completionHandler(UIImage(data: data), nil) } task.resume() } }
然后,當您需要 UI 圖像時,您可以懶惰地檢索它:
book.retrieveImage { image, error in DispatchQueue.main.async { image, error in cell.imageView.image = image } }
這些是您可以采用的幾種方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.