本主题旨在自己学习的过程中举出实例业务项目片段帮助自己提高灵性,深入领会知识概念,记作学习拓展笔记。希望在有幸被您看到文章的同时为您带来更多启示。
Hashable
如果我们想要将自定义结构体、类储存在 Set 中,或是将其作为 Dictionary 的键值 Key,可以继承 Hashable 并实现需要实现的内容。
例如实现:自定义多选 UI,选出去过的城市,将选中的城市存储在集合中。
先上Demo:
https://github.com/wiiale/AdvancedSwiftThinker/tree/master/T01-Hashable-Footprint
这里为了保持简单,直接tableView.reloadData()
了,也可以添加漂亮的 UI、动画效果来自定义的更加明显。
实现代码中主要与 Hasable 相关模块
实现
// City.swift
struct City {
let name: String
let province: String
}
extension City: Hashable {
var hashValue: Int {
get {
return "\(name)\(province)".hashValue
}
}
static func ==(lhs: City, rhs: City) -> Bool {
return lhs.name == rhs.name
}
}
使用
Set<City>: City 需要实现Hashable
才能存储在集合 Set 中。
// TableViewDataSource.swift
import UIKit
class TableViewControllerDataSource: NSObject, UITableViewDataSource {
var cities: [City]
// Set<City>
var selectedCitiesSet: Set<City>
weak var owner: TableViewController?
init(cities: [City], selectedCitiesSet: Set<City>, owner: TableViewController?) {
self.cities = cities
self.selectedCitiesSet = selectedCitiesSet
self.owner = owner
}
···
}
状态改变时,是否相等的比较避免不必要的状态更新,同时用到了实现 Hashable 时需要实现 Equatable 的重载==
操作符来进行判等操作。对于元素类型实现了 Equatable 协议的数组,也可以直接使用contains
方法会查看数组是否包含满足给定的谓词条件的元素等操作。
TableViewController.swift
func stateDidChanged(state: State, previousState: State?, command: Command?) {
if let command = command, case .loadCities(let handler) = command {
CityStore.shared.getCities(completionHandler: handler)
}
// 判等
if previousState == nil
|| previousState!.dataSource.cities != state.dataSource.cities
|| previousState!.dataSource.selectedCitiesSet != state.dataSource.selectedCitiesSet
{
tableView.dataSource = state.dataSource
tableView.reloadData()
}
}
注意点(1)-避免哈希碰撞
如果用不太稳妥的方式实现 hashValue 则会导致哈希碰撞,需要注意 hashValue 的实现方式。错误示例:
var hashValue: Int {
get {
return name.hashValue ^ province.hashValue
}
}
City(name: "Shanghai", province: "Shanghai").hashValue // 0
City(name: "Beijing", province: "Beijing") .hashValue // 0
City(name: "Hangzhou", province: "Zhejiang").hashValue // 6535861917244346740
City(name: "Zhejiang", province: "Hangzhou").hashValue // 6535861917244346740
注意点(2)-继承 NSObject 的类
NSObject 已继承 Equatable, Hashable,存在方法 isEqual,不需要重载==
运算符。
class CityObj: NSObject { ··· }
extension CityObj {
override var hashValue: Int {
get {
return "\(name)\(province)".hashValue
}
}
override func isEqual(_ object: Any?) -> Bool {
return name == (object as? CityObj)?.name
}
}
本文 ViewController 学习并尝试单向数据流动的方式编写,参考自喵神的单向数据流动的函数式 View Controller
本册文集中以“提出简单需求->简单实现需求片段”为流程,内容只针对该知识点实现的业务实例进行熟悉,业务也必定存在比文章方法更好的实现方式,文章旨在分析知识点并加深理解。文集不普及基本知识,不包含《Swift 进阶》书籍的详细内容。深入学习Swift请大家支持正版书籍(ObjC 中国)