有没有办法用 KVC 获取数组数组的所有第一个对象?
Is there a way to get all the first objects of an array of arrays with KVC?
假设我有一个数组,如:
NSArray *array = @[@[@1, @2], @[@3, @4], @[@5, @6]];
是否有 KVC 密钥路径可以给我@[@1, @3, @5]?
令我惊讶的是,虽然它依赖于一些未记录的行为。
NSArray
覆盖 -valueForKey:
的文档说它通过将 -valueForKey:
应用到具有提供的键和 returns 的每个元素来构建一个新数组。它确实不暗示任何键都是特殊的。
然而,NSDictionary
的覆盖确实表示以 @
开头的键被特殊对待。它不是查看字典的内容,而是查看字典本身的属性,以查找与删除前导 @
的键相匹配的字典。因此,您可以使用 [someDict valueForKey:@"@count"]
来获取字典中对象的数量。
事实证明,NSArray
实际上遵守相同的约定。您可以在示例中使用 [array valueForKey:@"@firstObject"]
来获取 @[@[@1, @2]]
。现在的问题是,如何将其应用于内部数组,而不是外部数组。
首先,如果您只调用 [array valueForKey:@"firstObject"]
而键上没有 @
会怎样?外部数组对每个内部数组调用 [innerArray valueForKey:@"firstObject"]
。每个内部数组依次尝试对其每个元素调用 [element valueForKey:@"firstObject"]
。但是,它的元素是 NSNumber
个对象,这些对象不符合密钥 "firstObject" 的 KVC。这样就爆炸了。
但是,KVC 支持 collection operators 和 -valueForKeyPath:
(注意最后的 "Path")。一位运算符是 @unionOfObjects
。这会将运算符右侧的路径部分应用于数组的元素,然后 returns 结果数组。这类似于 -valueForKey:
传播到内部数组的情况,只是您需要为内部数组指定不同的键。
因此,结合这些机制,您可以:[array valueForKeyPath:@"@unionOfObjects.@firstObject"]
得到 @[@1, @3, @5]
。外部数组将 [innerArray valueForKeyPath:@"@firstObject"]
应用于每个内部数组。由于前导 @
,它不会传播到内部数组的元素。它只是获取内部数组的 firstObject
。然后外部数组将它们组合成一个结果数组。
KVC 键路径仅在对象为 confirming Key Value coding compliance 时有效。表示 object , scaler 值应该在键名上有 setter 和 getter 。在您的结构中,索引路径没有键 getter 和 setter。所以我创建了自定义 getter 方法来满足内部对象索引路径的 NSKeyValueCoding。这将适用于内部数组的每个索引。
@interface NSArray (KVO)
- (id) unionOfObjectsForKeyPath:(NSString*)keyPath;
@end
@implementation NSArray (KVO)
- (id) unionOfObjectsForKeyPath:(NSString*)keyPath
{
NSMutableArray * values = [NSMutableArray new];
for (id obj in self) {
if ([obj isKindOfClass:[NSArray class]]) {
@try {
NSUInteger index = [keyPath integerValue];
id value = nil;
if (index < [(NSArray *)obj count]) {
value = [obj objectAtIndex:index];
if(value && value!=[NSNull null] )
{
[values addObject:value];
}
}
}
@catch (id) {}
}
}
return [NSArray arrayWithArray:values];
}
@end
在 VC 中:
self.myArray = @[@[@1, @2], @[@3, @4], @[@5, @6]];
NSArray *testArray =[self.myArray unionOfObjectsForKeyPath:@"0"];
NSLog(@"test array : %@", testArray);
假设我有一个数组,如:
NSArray *array = @[@[@1, @2], @[@3, @4], @[@5, @6]];
是否有 KVC 密钥路径可以给我@[@1, @3, @5]?
令我惊讶的是,虽然它依赖于一些未记录的行为。
NSArray
覆盖 -valueForKey:
的文档说它通过将 -valueForKey:
应用到具有提供的键和 returns 的每个元素来构建一个新数组。它确实不暗示任何键都是特殊的。
然而,NSDictionary
的覆盖确实表示以 @
开头的键被特殊对待。它不是查看字典的内容,而是查看字典本身的属性,以查找与删除前导 @
的键相匹配的字典。因此,您可以使用 [someDict valueForKey:@"@count"]
来获取字典中对象的数量。
事实证明,NSArray
实际上遵守相同的约定。您可以在示例中使用 [array valueForKey:@"@firstObject"]
来获取 @[@[@1, @2]]
。现在的问题是,如何将其应用于内部数组,而不是外部数组。
首先,如果您只调用 [array valueForKey:@"firstObject"]
而键上没有 @
会怎样?外部数组对每个内部数组调用 [innerArray valueForKey:@"firstObject"]
。每个内部数组依次尝试对其每个元素调用 [element valueForKey:@"firstObject"]
。但是,它的元素是 NSNumber
个对象,这些对象不符合密钥 "firstObject" 的 KVC。这样就爆炸了。
但是,KVC 支持 collection operators 和 -valueForKeyPath:
(注意最后的 "Path")。一位运算符是 @unionOfObjects
。这会将运算符右侧的路径部分应用于数组的元素,然后 returns 结果数组。这类似于 -valueForKey:
传播到内部数组的情况,只是您需要为内部数组指定不同的键。
因此,结合这些机制,您可以:[array valueForKeyPath:@"@unionOfObjects.@firstObject"]
得到 @[@1, @3, @5]
。外部数组将 [innerArray valueForKeyPath:@"@firstObject"]
应用于每个内部数组。由于前导 @
,它不会传播到内部数组的元素。它只是获取内部数组的 firstObject
。然后外部数组将它们组合成一个结果数组。
KVC 键路径仅在对象为 confirming Key Value coding compliance 时有效。表示 object , scaler 值应该在键名上有 setter 和 getter 。在您的结构中,索引路径没有键 getter 和 setter。所以我创建了自定义 getter 方法来满足内部对象索引路径的 NSKeyValueCoding。这将适用于内部数组的每个索引。
@interface NSArray (KVO)
- (id) unionOfObjectsForKeyPath:(NSString*)keyPath;
@end
@implementation NSArray (KVO)
- (id) unionOfObjectsForKeyPath:(NSString*)keyPath
{
NSMutableArray * values = [NSMutableArray new];
for (id obj in self) {
if ([obj isKindOfClass:[NSArray class]]) {
@try {
NSUInteger index = [keyPath integerValue];
id value = nil;
if (index < [(NSArray *)obj count]) {
value = [obj objectAtIndex:index];
if(value && value!=[NSNull null] )
{
[values addObject:value];
}
}
}
@catch (id) {}
}
}
return [NSArray arrayWithArray:values];
}
@end
在 VC 中:
self.myArray = @[@[@1, @2], @[@3, @4], @[@5, @6]];
NSArray *testArray =[self.myArray unionOfObjectsForKeyPath:@"0"];
NSLog(@"test array : %@", testArray);