NSArray 的 -valueForKey 上的问题:当它的项是 NSDictionary 时



我有一个数组,其中包含NSDictionary的项,我想将这些项转换为其他对象,我的第一个想法是valueForKey:,所以我为NSDictionary添加了一个类别方法toMyObject,并调用:

[array valueForKey:@"toMyObject"]

但它并没有像预期的那样工作,它只是返回NSNull s的数组。

如果我不想枚举数组,有什么想法可以解决这个问题吗?

回答我自己。dictionary的valueForKey:覆盖默认行为,如果dictionary没有密钥,它将返回nil,而不会像NSObject那样调用访问器方法,正如Apple文档所说:

若键不以"@"开头,则调用objectForKey:。如果钥匙有以"@"开头,去掉"@"并用调用[super-valueForKey:]钥匙的其余部分。

由于NSDictionary是一个集群类,因此不建议使用子类来覆盖行为。相反,我使用类似瑞士的方法:

@implementation NSDictionary (MyAddition)
static void swizzle(Class c, SEL orig, SEL new)
{
  Method origMethod = class_getInstanceMethod(c, orig);
  Method newMethod = class_getInstanceMethod(c, new);
  if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
    class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
  else
    method_exchangeImplementations(origMethod, newMethod);
}
+ (void)initialize
{
  if (self == [NSDictionary class]){
    swizzle([NSDictionary class],
            @selector(valueForKey:),
            @selector(myValueForKey:));
  }
}
- (id)toMyObject
{
  return toMyObject;
}
...
- (id)myValueForKey:(NSString *)key
{
  // for collection operators
  if ([key compare:@"@" options:0 range:NSMakeRange(0, 1)] == NSOrderedSame)
    return [super valueForKey:key];
  if ([key isEqualToString:@"toMyObject"])
    return [self toMyObject];
  return [self myValueForKey:key];
}

现在,NSArray调用valueForKey:@"toMyObject"是安全的。

又有一个没有swizzling的实现:

@implementation NSObject (MLWValueForKey)
- (id)mlw_valueForKey:(NSString *)key {
    if ([key hasPrefix:@"@"]) {
        return [self valueForKey:key];
    }
    NSAssert(![key containsString:@":"], @"Key should be selector without arguments");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    return [self performSelector:NSSelectorFromString(key)];
#pragma clang diagnostic pop
}
@end
@implementation NSArray (MLWValueForKey)
- (id)mlw_valueForKey:(NSString *)key {
    if ([key hasPrefix:@"@"]) {
        return [self valueForKey:key];
    }
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count];
    for (id object in self) {
        [array addObject:[object mlw_valueForKey:key]];
    }
    return array;
}
@end

最新更新