NSArray 的 -valueForKey 问题:当其项为 NSDictionary 时

发布于 2025-01-02 03:48:00 字数 305 浏览 0 评论 0原文

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

[array valueForKey:@"toMyObject"]

但它并没有按预期工作,它只是返回数组NSNull

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

I have an array which contains items of NSDictionary, I want to transform the items to other objects, my first thought is valueForKey:, so I add a category method toMyObject for NSDictionary, and call for:

[array valueForKey:@"toMyObject"]

But it doesn't work as expect, it just returns the array of NSNulls.

Any ideas to solve this problem if I don't want to enumerate the array?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

风尘浪孓 2025-01-09 03:48:00

回答我自己。字典的 valueForKey: 会覆盖默认行为,如果字典没有 key,则会返回 nil,不会像 Apple 那样像 NSObject 那样调用访问器方法文件说:

如果 key 不是以“@”开头,则调用 objectForKey:。如果钥匙有
以“@”开头,去掉“@”并调用 [super valueForKey:]
其余的密钥。

由于 NSDictionary 是一个集群类,因此不建议子类化来覆盖该行为。相反,我使用这样的 swiss 方法:

@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"

Answer to myself. The valueForKey: of dictionary overwrite the default behavior, if the dictionary doesn't have the key, it will return nil and not call the accessor method as NSObject do, as Apple document says:

If key does not start with “@”, invokes objectForKey:. If key does
start with “@”, strips the “@” and invokes [super valueForKey:] with
the rest of the key.

Since NSDictionary is a cluster class, it's not recommend to subclass to overwrite the behavior. Instead I use the method swiss like this:

@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];
}

Now it's safe for an NSArray to call valueForKey:@"toMyObject".

流绪微梦 2025-01-09 03:48:00

另一种无需调整的实现:

@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

One more implementation without 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文