4

このようにKVCでワイルドカードを使用しようとしています。

出来ますか?

または、メンバー変数を示すためにワイルドカードを使用する他の方法はありますか?

@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

ワイルドカードの使用

MyClass *testClass = [[[MyClass alloc] init] autorelease];
testClass.test1 = @50;

NSLog(@"test value : %@", [testClass valueForKey:@"*1"]);

詳細コード用。

私が望んでいた本当の理由は、インスタンスのメンバー変数を整数または nsnumber 型の値で示すことです。可能であれば、任意のインスタンスの値を設定したり、値を読み取ったりする方が簡単です。

たとえば、プロパティ部分のコピー。

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

2 に答える 2

4

正規表現を追加し、カテゴリを使用して、ワイルドカードを増やします。

これで正規表現がどのように機能するかについては、NSRegularExpression Class Referenceを参照してください。

特徴:

  • 多種多様なキーのマッチングに正規表現を使用
  • どのインスタンスでも機能するカテゴリを使用します
  • クラスごとにキーリストをキャッシュします
  • 完全な KVC サポート (プロパティだけでなく、アクセサー メソッドと iVar も!)
  • 現在の KVC メソッドと完全に統合します (キーが見つからない場合にのみ正規表現を使用し、パフォーマンスを向上させます)
  • @JamesWebsterのソリューションのように、サブクラス化はそれを台無しにしません
  • NSObjectのメソッドでキーのリストを不必要に汚染しません
  • 一致したキーと値の NSDictionary を返します

短所:

  • より遅く、理解するのがより複雑な正規表現を使用します
  • クラスの初期ルックアップが遅い (すべてのメソッドと iVar を反復処理する必要がある)
  • メソッドを自動的に上書きする-valueForUndefinedKey:ため、これにより既存のコードが壊れる可能性があります (修正するために独自のメソッドに移動します)。
  • 現在、値の設定はサポートされていません (設計上、これはまったく別の猫の袋です)。
  • 結果に重複したキーパスが含まれる可能性があります (最大の問題ではありませんが、KVC マッチングが複雑であり、すべてのルールを実装する必要があるという事実に起因します)
  • NSRegularExpressionこれは、iOS 4 以降でのみ使用できます (最大の問題ではありません) 。

バージョン履歴:

  • 1.0: 初期リリース

だから、ここにコードがあります:

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; }"
    }
}
于 2012-08-21T00:32:14.043 に答える
3

あなたが試みていた構文を使用してワイルドカードをサポートする方法が見つかりませんでしたが。Objective-C ランタイムを使用して、このラウンドアバウト メソッドを見つけました!

まず、使用したいクラスのすべてのプロパティを取得します

#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);

次に、実際に必要なものを除外します

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

それでは実際に使ってみましょう

for (NSString *key in array)
    NSLog(@"%@", [testClass valueForKey:key]);
于 2012-08-20T15:45:31.267 に答える