正規表現を追加し、カテゴリを使用して、ワイルドカードを増やします。
これで正規表現がどのように機能するかについては、NSRegularExpression Class Referenceを参照してください。
特徴:
- 多種多様なキーのマッチングに正規表現を使用
- どのインスタンスでも機能するカテゴリを使用します
- クラスごとにキーリストをキャッシュします
- 完全な KVC サポート (プロパティだけでなく、アクセサー メソッドと iVar も!)
- 現在の KVC メソッドと完全に統合します (キーが見つからない場合にのみ正規表現を使用し、パフォーマンスを向上させます)
- @JamesWebsterのソリューションのように、サブクラス化はそれを台無しにしません
NSObject
のメソッドでキーのリストを不必要に汚染しません
- 一致したキーと値の NSDictionary を返します
短所:
- より遅く、理解するのがより複雑な正規表現を使用します
- クラスの初期ルックアップが遅い (すべてのメソッドと iVar を反復処理する必要がある)
- メソッドを自動的に上書きする
-valueForUndefinedKey:
ため、これにより既存のコードが壊れる可能性があります (修正するために独自のメソッドに移動します)。
- 現在、値の設定はサポートされていません (設計上、これはまったく別の猫の袋です)。
- 結果に重複したキーパスが含まれる可能性があります (最大の問題ではありませんが、KVC マッチングが複雑であり、すべてのルールを実装する必要があるという事実に起因します)
NSRegularExpression
これは、iOS 4 以降でのみ使用できます (最大の問題ではありません) 。
バージョン履歴:
だから、ここにコードがあります:
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
例:
@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)[A-J].*"; // match (case insensitive) a key starting with A - J
NSLog(@"%@", [obj valueForKey:regex]); // prints "{ aKey = value; isProperty = 1; }"
}
}