在实际项目中,我们时常需要统计比如学生的平均分数,最高分,最低分,以及同学们都来自哪些不重复的城市等。此时我么可能需要用到循环计算,但一门优雅的语言应该有效的避免循环,因为很多时候 for, while快速枚举等都会显得很累赘。幸好Cocoa提供了键值编码来优雅的解决这类问题。
由于这个点比较简单,所以不赘述原理了,能读到这篇文章说明对KVC已经有一定了解,下面直接说重点。
KVC中的集合运算符有以下三类:
@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") // 离现在最近的那个日期 |
1 2 3 4 | // 此处为找出所有人名字的数组,所有人名字去重的数组,所有人生日不重复的数组,返回的是.key对应的值数组,不是对象数组 usersNSArray.value(forKeyPath: "@unionOfObjects.name") usersNSArray.value(forKeyPath: "@distinctUnionOfObjects.name") usersNSArray.value(forKeyPath: "@distinctUnionOfObjects.birthDate") |
返回一个数组,值由各个子数组的元素组成,不去重
返回一个数组,值由各个子数组的元素组成,去重
和@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,或自行编码进行验证。