一、什么是集合代理对象
假设我们有这样一个问题:我们有一NSMutableArray的对象,我们希望在往插入对象的时候出发KVO,改怎么实现呢?
我们知道KVO是通过隐式的创建一个子类,重写setter方法实现的(见KVO篇)。而往数组里插入,删除对象的时候是不会出发setter方法的,所以是触发不了KVO的。那么我们应该怎么解决呢? 那就是使用KVC的集合代理对象。既然是代理对象,那就不是对象本身了,而是一个代理对象。举个栗子:假设我们有个商品类:Product,我们需要通过 -valueForKey:
方法获取一个NSArray,NSSet或者NSOrderedSet的集合,而Product中并没有实现 -<key>
方法 那么我们可以在Product类中这样写:
- (NSInteger)countOfContacts;
- (id)objectInContactsAtIndex:(NSInteger)index;
这样我们在调用[product valueForKey:@"contacts"]
的时候就会返回给我们一个NSArray的代理对象(注意是代理对象哦,不一定是NSArray),我们可以像数组一样去操作这个对象。
如果对NSSet,NSOrderedSet 实现相同的功能,则需要实现如下方法:
NSArray | NSSet | NSOrderedSet |
---|---|---|
-countOf<Key> |
-countOf<Key> |
-countOf<Key> |
-enumeratorOf<Key> |
-indexIn<Key>OfObject: |
|
以下两者二选一 | -memberOf<Key>: |
|
-objectIn<Key>AtIndex: |
以下两者二选一 | |
-<key>AtIndexes: |
-objectIn<Key>AtIndex: |
|
-<key>AtIndexes: |
||
可选(增强性能) | ||
-get<Key>:range: |
可选(增强性能) | |
-get<Key>:range: |
一句话概括集合代理对象就是:KVC通过上面表格里面的方法相互组合来完成集合具备的功能。
对于可变的集合还需要实现如下的功能方法:
NSMutableArray / NSMutableOrderedSet | NSMutableSet |
---|---|
至少实现一个插入方法和一个删除方法 | 至少实现一个插入方法和一个删除方法 |
-insertObject:in<Key>AtIndex: |
-add<Key>Object: |
-removeObjectFrom<Key>AtIndex: |
-remove<Key>Object: |
-insert<Key>:atIndexes: |
-add<Key>: |
-remove<Key>AtIndexes: |
-remove<Key>: |
可选(增强性能)以下方法二选一 | 可选(增强性能) |
-replaceObjectIn<Key>AtIndex:withObject: |
-intersect<Key>: |
-replace<Key>AtIndexes:with<Key>: |
-set<Key>: |
二、使用场景
1、KVO集合元素变化时,使用-mutableArrayValueForKey:
生成数组代理对象(这是个面试题)
[product mutableArrayValueForKey:@"contacts"] addObject:newcontact];
mutableArrayValueForKey:会生成数组contactsde代理对象,当我们addObject的时候,会生成一个新的数组,代替原来的数组,这样就会触发KVO了。
2、以集合的方式操作操作非集合对象时,实现上面两个表格中的特定方法即可。这里放一个网上的例子:
@interface Primes : NSObject
@property (readonly, nonatomic, strong) NSArray *primes;
@end
@implementation Primes
static int32_t const primes[] = {
2, 101, 233, 383, 3, 103, 239, 389, 5, 107, 241, 397, 7, 109,
251, 401, 11, 113, 257, 409, 13, 127, 263, 419, 17, 131, 269,
421, 19, 137, 271, 431, 23, 139, 277, 433, 29, 149, 281, 439,
31, 151, 283, 443, 37, 157, 293, 449, 41, 163, 307, 457, 43,
167, 311, 461, 47, 173, 313, 463, 53, 179, 317, 467, 59, 181,
331, 479, 61, 191, 337, 487, 67, 193, 347, 491, 71, 197, 349,
499, 73, 199, 353, 503, 79, 211, 359, 509, 83, 223, 367, 521,
89, 227, 373, 523, 97, 229, 379, 541, 547, 701, 877, 1049,
557, 709, 881, 1051, 563, 719, 883, 1061, 569, 727, 887,
1063, 571, 733, 907, 1069, 577, 739, 911, 1087, 587, 743,
919, 1091, 593, 751, 929, 1093, 599, 757, 937, 1097, 601,
761, 941, 1103, 607, 769, 947, 1109, 613, 773, 953, 1117,
617, 787, 967, 1123, 619, 797, 971, 1129, 631, 809, 977,
1151, 641, 811, 983, 1153, 643, 821, 991, 1163, 647, 823,
997, 1171, 653, 827, 1009, 1181, 659, 829, 1013, 1187, 661,
839, 1019, 1193, 673, 853, 1021, 1201, 677, 857, 1031,
1213, 683, 859, 1033, 1217, 691, 863, 1039, 1223, 1229,
};
- (NSUInteger)countOfPrimes;
{
return (sizeof(primes) / sizeof(*primes));
}
- (id)objectInPrimesAtIndex:(NSUInteger)idx;
{
NSParameterAssert(idx < sizeof(primes) / sizeof(*primes));
return @(primes[idx]);
}
@end
我们将会运行以下代码:
Primes *primes = [[Primes alloc] init];
NSLog(@"The last prime is %@", [primes.primes lastObject]);
三、集合操作符
这部分内容不想赘述,以下部分类容参照https://www.jianshu.com/p/7675709d14a7 ,感谢这位作者付出。
集合操作:一个集合/数组通过调用valueForKeyPath:
可允许一个集合中的对象属性根据集合操作符做相应的操作。集合操作符是一个以@
开头特殊的字符串,下面是集合操作的格式插图(来自官方文档):

Operator key path format
注意:所有的集合操作,除了@count
,其他都需要有右边的keyPath(一般为属性名),目前还不支持自定义集合操作符
集合操作符分为三种:
-
简单的集合操作 返回NSString、NSNumber、NSDate
-
对象操作符 返回NSArray
-
数组或集合操作符 返回NSArray、NSSet
Simple Collection Operators(简单的操作符)
-
@avg 平均值
-
@count 个数
-
@max 最大值
-
@min 最小值
-
@sum 和
// 平均身高 float avg = [[students valueForKeyPath:@"@avg.height"] floatValue]; // 身高总和 float sum = [[students valueForKeyPath:@"@sum.height"] floatValue]; // 最大值 float max = [[students valueForKeyPath:@"@max.height"] floatValue]; // 最小值 float min = [[students valueForKeyPath:@"@min.height"] floatValue]; // 个数 float count = [[students valueForKeyPath:@"@count"] floatValue];
Object Operator (对象操作符)
-
@distinctUnionOfObjects 返回一个由操作符右边的key path所指定的对象属性组成的数组,不对数组去重
-
@unionOfObjects 返回一个由操作符右边的key path所指定的对象属性组成的数组,并对数组去重
NSArray *resultsOne = [studentsOfClassOne valueForKeyPath:@"@unionOfObjects.height"];
NSArray *resultsTwo = [studentsOfClassTwo valueForKeyPath:@"@distinctUnionOfObjects.height"];
Array and Set Operators(数组和集合操作符)
-
@distinctUnionOfArrays/ @unionOfArrays: 返回NSArray,distinct版本会对数组取重
-
@distinctUnionOfSets: 返回一个NSSet对象,因为集合不能包含重复的值,所以它只有distinct操作。
// 去除相同值的数组 NSArray *distinctResults = [@[studentsOfClassOne, studentsOfClassTwo] valueForKeyPath:@"@distinctUnionOfArrays.height"];
网友评论