学习SwiftUI过程中,发现官方文档中使用的几个协议,了解一下做了记录。
Equatable
首先是,Equatable协议,顾名思义,就是用来方便比较判等的一个协议,使用如下:
// 遵循Equatable协议,重载==方法
class StreetAddress: Equatable {
let number: String
let street: String
let unit: String?
init(_ number: String, _ street: String, unit: String? = nil) {
self.number = number
self.street = street
self.unit = unit
}
public static func == (lhs: StreetAddress, rhs: StreetAddress) -> Bool {
// 需要比较的值
return lhs.number == rhs.number && lhs.street == rhs.street
}
}
调用如下:
let addresses = [StreetAddress("1490", "Grove Street"),
StreetAddress("2119", "Maple Avenue"),
StreetAddress("1400", "16th Street")
]
let home = StreetAddress("1400", "16th Street")
print(addresses.contains(home))
输出结果为true
*对象的比较,原本比较的是两者的内存地址是一样的,很多时候这显然满足不了我们的需求,通过实现Equatable协议的方法,我们可以满足我们的需求,比较出两个对象属性值相等的情况。
Hashable
Swift特性你可以使用自定义的类型作为合集的值或者字典的key,只需要让他遵循Hashable协议,Hashable遵从Equatable协议

添加进集合Set的值必须遵守Hashable协议,下图的StreetAddress类只遵守了Equatable协议。

定义遵循Hashable协议的结构体GridPoint:
struct GridPoint: Hashable {
var x: Int
var y: Int
static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
return lhs.x == rhs.x && lhs.y == rhs.y
}
// 实现改法替代hashvalue
func hash(into hasher: inout Hasher) {
hasher.combine(x)
hasher.combine(y)
}
}
添加进集合中使用:
var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)]
let nextTap = GridPoint(x: 0, y: 1)
let newTap = GridPoint(x: 1, y: 1)
if tappedPoints.contains(nextTap) {
print("Already tapped at (\(nextTap.x), \(nextTap.y)).")
} else {
tappedPoints.insert(nextTap)
print("New tap detected at (\(nextTap.x), \(nextTap.y)).")
}
输出结果:

key使用:
let dict = [newTap: "888", nextTap: "ttt"]
print(dict[newTap])
控制台输出:

Codable

Codable协议主要用于对对象的编码与解码,从上图可以看出Codable协议是Decodable和Encodable的重命名。
解析JSON
Codable的使用例子我使用的是官方SwiftUI叫教程中的代码:
import SwiftUI
import CoreLocation
struct Landmark: Hashable, Codable {
var id: Int
var name: String
fileprivate var imageName: String
fileprivate var coodinates: Coordinates
var state: String
var park: String
var category: Category
var locationCoordiante: CLLocationCoordinate2D {
CLLocationCoordinate2D(latitude: coodinates.latitude, longitude: coodinates.longtitude)
}
enum Category: String, CaseIterable, Codable {
case featured = "Featured"
case lakes = "Lakes"
case rivers = "Rivers"
}
}
struct Coordinates: Hashable, Codable {
var latitude: Double
var longtitude: Double
}
使用JSONDecoder解码:
let landmarkData: [Landmark] = load("landmarkData.json")
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
let t = try decoder.decode(T.self, from: data)
return t
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
需要注意的是:
*枚举类型想要遵循Codable协议需要有初始值类型。
*假如转换过程有的字段命名不相符的话,需要实现CodingKeys枚举值
enum CodingKeys: String, CodingKey {
case state = "status"
// 这边省略了,
case
}
同样需要注意的是,这里即便是json与model相符的也需要全部写上,否则会报错

属性列表(PropertyList)
Codable协议同样也支持plist文件格式,解析和归档plist文件与json的使用方法相似,只需要将JSONEncoder和JSONDecoder替换成对应的 PropertyListEncoder和 PropertyListDecoder即可。
CaseIterable
老实说这个协议顾名思义我是顾不出来了,三级英语跟不上了。略做了解,发现该协议是用来合成简单枚举类型的allCases静态属性,文档中声明如下:

enum Category: String, CaseIterable, Codable {
case featured = "Featured"
case lakes = "Lakes"
case rivers = "Rivers"
}
print(Category.allCases)

当然我们也可以自己实现:
static var allCases:[Category] {
return [.featured, .lakes, .rivers]
}
Demo地址:github.com/ZpFate/Equa…