Can I use a wildcard in KVC?

I am trying to use a wildcard in KVC like this.

Is it possible?

Or are there other ways to use the template to specify a member variable?

@interface MyClass : NSObject @property(nonatomic, retain) NSNumber *test1; @property(nonatomic, retain) NSNumber *test2; @end @implementation MyClass{ NSNumber * test1; NSNumber * test2; } @synthesize test1; @synthesize test2; @end 

using a wildcard

 MyClass *testClass = [[[MyClass alloc] init] autorelease]; testClass.test1 = @50; NSLog(@"test value : %@", [testClass valueForKey:@"*1"]); 

For detailed codes.

The real reason I want to is to specify an instance member variable by value of type integer or nsnumber. If possible, it is easier to set values ​​and read the values ​​of any instance.

For example, a copy of part of the property.

 MyClass *testClass = [[[MyClass alloc] init] autorelease]; testClass.year_1 = @2012; testClass.quarter_2 = @3; testClass.month_3 = @8; testClass.day_4 = @20; testClass.week_5 = @4; // copy propertys to other instance. // Normal way MyClass *testClassCopy = [[[MyClass alloc] init] autorelease]; testClassCopy.year_1 = testClass.year_1; testClassCopy.quarter_2 = testClass.quarter_2; testClassCopy.month_3 = testClass.month_3; testClassCopy.day_4 = testClass.day_4; // copy propertys by using wildcard for (int j = 0; j < 4; j++) { NSString *indicate = [NSString stringWithFormat:@"*%@", [NSNumber numberWithInteger:j + 1]]; NSNumber *sourceProperty = [testClass valueForKey:indicate]; [testClassCopy setValue:sourceProperty forKey:indicate]; } 
+4
source share
2 answers

Although I could not find a way to support wildcards using the syntax you were trying to do. I found this workaround using Objective-C runtime!

First we get all the properties of the class that you would like to use

 #import <objc/runtime.h> unsigned int outCount; objc_property_t *properties = class_copyPropertyList([MyClass class], &outCount); NSMutableArray *array = [NSMutableArray arrayWithCapacity:outCount]; for (int i = 0; i < outCount; i++) { objc_property_t property = properties[i]; const char *propName = property_getName(property); if(propName) { NSString *propertyName = [NSString stringWithUTF8String:propName]; [array addObject:propertyName]; } } free(properties); 

Then filter out the ones you really want

 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF ENDSWITH '1'"]; [array filterUsingPredicate:predicate]; 

Then actually use them

 for (NSString *key in array) NSLog(@"%@", [testClass valueForKey:key]); 
+3
source

I will raise your wildcards by adding Regex and using categories:

To find out how the regular expression works, read the NSRegularExpression class reference .

Features:

  • Uses a regex to match a wide range of keys
  • Uses a category that works in any instance.
  • Cache Lists for Each Class
  • Full support for KVC (not only properties, but also access methods and iVars!)
  • Integrates flawlessly with current KVC methods (only regular expression is used if the key is not found, which improves performance)
  • Subclass won't mess it up like @JamesWebster solution
  • Inevitably pollutes the key list with NSObject methods
  • Returns NSDictionary of matching keys and values

Minuses:

  • Uses a regex that is slower and harder to understand
  • Slow initial lookup for a class (must go through all methods and iVars)
  • Automatically overwrites the -valueForUndefinedKey: method, so it’s possible that this might break existing code (move it to your own method to fix it).
  • Currently, setting values ​​is not supported (by design, that all other bag of cats).
  • May have a duplicate keyPaths as a result (not the biggest of the problems, but stems from the fact that KVC compliance is complex and I have to implement all the rules).
  • Uses NSRegularExpression , which is only available in iOS 4 and later (not the biggest of problems).

Version History:

  • 1.0: initial version

So here is the code:

NSObject + KVCRegex.h:

 // // NSObject+KVCRegex.h // TestProj // // Created by Richard Ross on 8/20/12. // Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved. // #import <Foundation/Foundation.h> @interface NSObject (KVCRegex) // custom implemenation -(id) valueForUndefinedKey:(NSString *)key; @end 

NSObject + KVCRegex.m:

 // // NSObject+KVCRegex.m // TestProj // // Created by Richard Ross on 8/20/12. // Copyright (c) 2012 Ultimate Computer Services, Inc. All rights reserved. // #import "NSObject+KVCRegex.h" #import <objc/runtime.h> @implementation NSObject (KVCRegex) static NSSet *keyPathsForClass(Class cls) { NSMutableSet *keys = [NSMutableSet set]; do { if (cls == [NSObject class]) { // nothing good can come from trying to use KVC on NSObject methods break; } unsigned count = 0; Method *methods = class_copyMethodList(cls, &count); for (int i = 0; i < count; i++) { // make sure that the method returns a value const char *methodName = sel_getName(method_getName(methods[i])); char returnType[64]; method_getReturnType(methods[i], returnType, 64); if (strcmp(returnType, "v") == 0) continue; // make sure that the method takes no args (except for self & _cmd) if (method_getNumberOfArguments(methods[i]) == 2) { // add a duplicate entry for ones matching 'is' if (strstr(methodName, "is") == methodName) { char *newStr = strdup(methodName + 2); newStr[0] = tolower(newStr[0]); [keys addObject:[NSString stringWithUTF8String:newStr]]; free(newStr); } [keys addObject:[NSString stringWithUTF8String:methodName]]; } } free(methods); // now copy iVars count = 0; Ivar *ivars = class_copyIvarList(cls, &count); for (int i = 0; i < count; i++) { const char *ivarName = ivar_getName(ivars[i]); if (strstr(ivarName, "_") == ivarName) [keys addObject:[NSString stringWithUTF8String:ivarName + 1]]; // iVar name starting with _<key> [keys addObject:[NSString stringWithUTF8String:ivarName]]; } free(ivars); } while ((cls = [cls superclass])); return [NSSet setWithSet:keys]; } // returns a dictionary based on 'key' as a regex -(id) valueForUndefinedKey:(NSString *)key { // lookup for later use static NSMutableDictionary *keyClassPairs; if (!keyClassPairs) keyClassPairs = [NSMutableDictionary dictionary]; if (!keyClassPairs[[self class]]) { keyClassPairs[(id<NSCopying>)[self class]] = keyPathsForClass([self class]); } NSSet *keyPaths = keyClassPairs[[self class]]; // assume 'key' is a regex NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:key options:0 error:nil]; NSMutableArray *matches = [NSMutableArray array]; for (NSString *keyPath in keyPaths) { NSRange matchRange = [regex rangeOfFirstMatchInString:keyPath options:0 range:(NSRange) { 0, keyPath.length }]; if (matchRange.length == keyPath.length) { // we have a match [matches addObject:keyPath]; } } if (matches.count) return [self dictionaryWithValuesForKeys:matches]; else [NSException raise:NSUndefinedKeyException format:@"Could not find a key that matches the regex in %@", key]; return nil; } @end 

Example:

 @interface MyObject : NSObject { @public int normalIvar; id _underscoreIvar; } @property id someProp; @property BOOL isProperty; @property int nativeProp; -(void) notAKey; -(id) aKey; @end @implementation MyObject @synthesize someProp, isProperty, nativeProp; -(void) notAKey { NSLog(@"Not a key!"); } -(id) aKey { return @"Value"; } @end int main() { @autoreleasepool { MyObject *obj = [MyObject new]; obj.someProp = @"a property"; obj.nativeProp = 15; obj.isProperty = YES; obj->normalIvar = 172; obj->_underscoreIvar = @"Ivar"; NSString *regex = @"[a|s].*"; // match a key starting with 'a' or 's', then matching anything else after NSLog(@"%@", [obj valueForKey:regex]); // prints "{ aKey = 'Value', someProp = 'a property' }" regex = @"_.*"; // match a key starting with '_', and then match anything else after NSLog(@"%@", [obj valueForKey:regex]); // prints "{ _underscoreIvar = 'Ivar' }" regex = @".*"; // match any key declared for this object NSLog(@"%@", [obj valueForKey:regex]); // prints "{ "_underscoreIvar" = Ivar; aKey = Value; isProperty = 1; nativeProp = 15; normalIvar = 172; property = 1; someProp = "a property"; underscoreIvar = Ivar; }" regex = @"(?i)[AJ].*"; // match (case insensitive) a key starting with A - J NSLog(@"%@", [obj valueForKey:regex]); // prints "{ aKey = value; isProperty = 1; }" } } 
+4
source

All Articles