Reputation: 4248
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 NSNull
s.
Any ideas to solve this problem if I don't want to enumerate the array?
Upvotes: 2
Views: 2383
Reputation: 18745
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
Upvotes: 0
Reputation: 4248
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"
.
Upvotes: 1