Swift几种协议使用(Equatable Hashable等)

6,623 阅读3分钟

学习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协议

Hashable遵从Equatable协议
Hashable遵从Equatable协议

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

集合无法添加没有遵守Hashable协议的值
集合无法添加没有遵守Hashable协议的值

定义遵循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协议
Codable协议主要用于对对象的编码与解码,从上图可以看出Codable协议是DecodableEncodable的重命名。

解析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 
    }

同样需要注意的是,这里即便是jsonmodel相符的也需要全部写上,否则会报错

报错
CodingKeys报错

属性列表(PropertyList)

Codable协议同样也支持plist文件格式,解析和归档plist文件与json的使用方法相似,只需要将JSONEncoderJSONDecoder替换成对应的 PropertyListEncoderPropertyListDecoder即可。

CaseIterable

老实说这个协议顾名思义我是顾不出来了,三级英语跟不上了。略做了解,发现该协议是用来合成简单枚举类型的allCases静态属性,文档中声明如下:

CaseIterable协议声明
CaseIterable协议声明

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

allCases输出
allCases输出

当然我们也可以自己实现:

    static var allCases:[Category] {
        return [.featured, .lakes, .rivers]
    }

Demo地址:github.com/ZpFate/Equa…