iOS复习笔记——KVC的集合操作

在实际项目中,我们时常需要统计比如学生的平均分数,最高分,最低分,以及同学们都来自哪些不重复的城市等。此时我么可能需要用到循环计算,但一门优雅的语言应该有效的避免循环,因为很多时候 for, while快速枚举等都会显得很累赘。幸好Cocoa提供了键值编码来优雅的解决这类问题。

由于这个点比较简单,所以不赘述原理了,能读到这篇文章说明对KVC已经有一定了解,下面直接说重点。

KVC中的集合运算符有以下三类:

1. 简单集合运算符

@avg,@sum,@max,@min,@count, 在Swift3以前和OC中只能操作NSObject的子类集合,如NSArray,NSSet等,下面结合例子说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// Swift Array 在Swift3中不能直接使用KVC,所以在下面转换为NSArray
let numberArray: [NSNumber] = [NSNumber(value: 100),
NSNumber(value: 100),
NSNumber(value: 200),
NSNumber(value: 300),
NSNumber(value: 400),
NSNumber(value: 500),
NSNumber(value: 600),
NSNumber(value: 700),
NSNumber(value: 800),
NSNumber(value: 900)]

// 在自身为NSNumber,NSDate等数据类型时,可加.self访问自身数据
NSArray(array: numberArray).value(forKeyPath: "@min.self") // 最小值
NSArray(array: numberArray).value(forKeyPath: "@max.self") // 最大值
NSArray(array: numberArray).value(forKeyPath: "@avg.self") // 平均值
NSArray(array: numberArray).value(forKeyPath: "@sum.self") // 累加的总量
NSArray(array: numberArray).value(forKeyPath: "@count.self") // 等同于Array.count

// Swift3 以前必须继承于NSObject,Swift4提供了一种更安全高效的KVC,后续再写
class LGUserInfo: NSObject {
override init() {
super.init()
}

var name: String?
var height: NSNumber?
var birthDate: Date?

convenience init(name: String, height: NSNumber, birthDate: Date) {
self.init()
self.height = height
self.name = name
self.birthDate = birthDate
}

func accessInstanceVariablesDirectly() -> Bool {
return true
}
}

let laogong = LGUserInfo(name: "laogong", height: 153, birthDate: Date(timeIntervalSince1970: 0))

let laoli = LGUserInfo(name: "laoli", height: 183, birthDate: Date(timeIntervalSince1970: 86400))

let ergou = LGUserInfo(name: "ergou", height: 226, birthDate: Date(timeIntervalSince1970: 8640000))

let kurt = LGUserInfo(name: "kurt", height: 168, birthDate: Date(timeIntervalSince1970: 86400000))

// 普通的取值赋值
kurt.value(forKey: "name")
kurt.value(forKeyPath: "birthDate.hashValue")
kurt.setValue("Kurt", forKey: "name")

let usersArray = [laogong, laoli, ergou, kurt, kurt]

let usersNSArray = NSArray(array: usersArray)

usersNSArray.value(forKeyPath: "@avg.height") // 身高的平均值
usersNSArray.value(forKeyPath: "@sum.height") // 总身高
usersNSArray.value(forKeyPath: "@max.height") // 最高的值
usersNSArray.value(forKeyPath: "@min.height") // 最低的值

// 这三个实际上是一样的效果,都是数组的元素个数
usersNSArray.value(forKeyPath: "@count")
usersNSArray.value(forKeyPath: "@count.height")
usersNSArray.count

usersNSArray.value(forKeyPath: "@min.birthDate") // 离现在最远的那个日期
usersNSArray.value(forKeyPath: "@max.birthDate") // 离现在最近的那个日期

2. 对象操作符:

  • @unionOfObjects:返回指定属性的值的数组,不去重
  • @distinctUnionOfObjects:返回指定属性去重后的值的数组
1
2
3
4
// 此处为找出所有人名字的数组,所有人名字去重的数组,所有人生日不重复的数组,返回的是.key对应的值数组,不是对象数组
usersNSArray.value(forKeyPath: "@unionOfObjects.name")
usersNSArray.value(forKeyPath: "@distinctUnionOfObjects.name")
usersNSArray.value(forKeyPath: "@distinctUnionOfObjects.birthDate")

3. 数组和集合运算符:

  • @unionOfArrays:

返回一个数组,值由各个子数组的元素组成,不去重

  • @distinctUnionOfArrays:

返回一个数组,值由各个子数组的元素组成,去重

  • @distinctUnionOfSets:

和@distinctUnionOfArrays差不多, 只是它期望的是一个包含着NSSet对象的NSSet,并且会返回一个NSSet对象。因为集合不能有重复的值,所以只有distinct操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let u1 = LGUserInfo(name: "user1", height: 123, birthDate: Date(timeIntervalSince1970: 100))
let u2 = LGUserInfo(name: "user2", height: 168, birthDate: Date(timeIntervalSince1970: 86400))
let u3 = LGUserInfo(name: "user3", height: 159, birthDate: Date(timeIntervalSince1970: 86400))
let u4 = LGUserInfo(name: "user4", height: 226, birthDate: Date(timeIntervalSince1970: 1000000))
let u5 = LGUserInfo(name: "user5", height: 158, birthDate: Date(timeIntervalSince1970: 86400000))

let usersArray2 = [u1, u2, u3, u4, u5, kurt]

let usersNSArray2 = NSArray(array: usersArray2)

NSArray(array: [usersNSArray, usersNSArray2]).value(forKeyPath: "@unionOfArrays.name")
NSArray(array: [usersNSArray, usersNSArray2]).value(forKeyPath: "@distinctUnionOfArrays.name") // 找出两个数组内不重复的人名并返回

NSArray(array: [usersNSArray, usersNSArray2]).value(forKeyPath: "@distinctUnionOfArrays.birthDate") // 找出两个数组内不重复的生日并返回

let usersSet = NSSet(array: usersArray)
let usersSet2 = NSSet(array: usersArray2)

NSSet(array: [usersSet, usersSet2]).value(forKeyPath: "@distinctUnionOfSets.name")

NSSet(array: [usersSet, usersSet2]).value(forKeyPath: "@distinctUnionOfSets.height")

上面KVC可以很方便的进行取值,运算,组合,去重等,那能不能通过KVC找出比如身高高于180的所有人呢,或者1991年以后出生的人呢?
答案是可以的,参见国外大神神奇的文章

这个解决方案虽然神奇,但并不是一个优雅的解决方案,那什么是优雅的解决方案呢?上一篇文章实际上已经说明了,参见谓词

本文所有代码均为列出执行结果,如果想看实际的运行结果,请点击此处下载本文playground,或自行编码进行验证。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注