12

実行時に任意の数の動的プロパティを持つことができるObjective-Cクラスを作成することは可能ですか?

クラス内でこれを呼び出してインターセプトして、例外を発生させて実行時に(たとえば)をmySpecialClass.anyProperty返すことができる独自のカスタム実装を提供できるようにしたいと思います。NSString明らかに、これはすべてコンパイルする必要があります。

理想的なのは、新しいリテラル構文に似たものを使用してプロパティを参照できる場合ですmySpecialClass["anyProperty"]

ある意味で、CFDictionaryバッキングストアのない動的NSDictionaryのようなものを作成したいと思います。これは、プロパティの取得と設定でそれぞれ2つのカスタムメソッドを実行し、プロパティ名をこれらのアクセサーメソッドに渡して、何をするかを決定できるようにします。

4

3 に答える 3

34

これを行うには、少なくとも2つの方法があります。

添え字

使用objectForKeyedSubscript:してsetObject:forKeyedSubscript:

 @property (nonatomic,strong) NSMutableDictionary *properties;

 - (id)objectForKeyedSubscript:(id)key {
      return [[self properties] valueForKey:[NSString stringWithFormat:@"%@",key]];
 }

 - (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key {
      [[self properties] setValue:object forKey:[NSString stringWithFormat:@"%@",key]];
 }

 Person *p = [Person new];
 p[@"name"] = @"Jon";
 NSLog(@"%@",p[@"name"]);

resolveInstanceMethod:

これは、すべてのメソッドに対してランタイムによって実行されるobjc_sendMsgです。

objc_sendMsg

下部を見ると、を実行する機会がresolveInstanceMethod:あります。これにより、メソッド呼び出しを選択したものの1つにリダイレクトできます。質問に答えるには、辞書ivarで値を検索する一般的なゲッターとセッターを作成する必要があります。

// generic getter
static id propertyIMP(id self, SEL _cmd) {
    return [[self properties] valueForKey:NSStringFromSelector(_cmd)];
}


// generic setter
static void setPropertyIMP(id self, SEL _cmd, id aValue) {

    id value = [aValue copy];
    NSMutableString *key = [NSStringFromSelector(_cmd) mutableCopy];

    // delete "set" and ":" and lowercase first letter
    [key deleteCharactersInRange:NSMakeRange(0, 3)];
    [key deleteCharactersInRange:NSMakeRange([key length] - 1, 1)];
    NSString *firstChar = [key substringToIndex:1];
    [key replaceCharactersInRange:NSMakeRange(0, 1) withString:[firstChar lowercaseString]];

    [[self properties] setValue:value forKey:key];
}

次に、実装resolveInstanceMethod:して、要求されたメソッドをクラスに追加します。

+ (BOOL)resolveInstanceMethod:(SEL)aSEL {
    if ([NSStringFromSelector(aSEL) hasPrefix:@"set"]) {
        class_addMethod([self class], aSEL, (IMP)setPropertyIMP, "v@:@");
    } else {
        class_addMethod([self class], aSEL,(IMP)propertyIMP, "@@:");
    }
    return YES;
}

メソッドのNSMethodSignatureを返すこともできます。これは、NSInvocationでラップされ、に渡されforwardInvocation:ますが、メソッドの追加はより高速です。

これがCodeRunnerで実行される要点ですmyClass["anyProperty"]呼び出しは処理しません。

于 2012-11-30T13:56:21.543 に答える
3

あなたはさまざまなことを求めています。クラスのインスタンスでブラケット構文を使用できるようにするmySpecialClass[@"anyProperty"]場合は、非常に簡単です。メソッドを実装するだけです。

 - (id)objectForKeyedSubscript:(id)key
 {
      return ###something based on the key argument###
 }

 - (void)setObject:(id)object forKeyedSubscript:(id <NSCopying>)key
 {
      ###set something with object based on key####
 }

ソースコードでブラケット構文を使用するたびに呼び出されます。

それ以外の場合、実行時にプロパティを作成する場合は、さまざまな方法で続行するか、NSObjectforwardInvocation:メソッドを確認するか、クラスを動的に変更する関数のObjective-Cランタイムリファレンスを確認してください。

于 2012-11-30T13:09:04.407 に答える
1

ギヨームは正しいです。forwardInvocation:行く方法です。この回答は、いくつかの詳細を提供します:objective-cのmethod_missingのような機能(つまり、実行時の動的委任)

これにはさらに詳細があります:Objective C/iOSのRubymethod_missingと同等です

そして、これらはあなたを助けるかもしれない他のあまり知られていないObj-C機能です:Objective-Cの隠された機能

楽しみ!

于 2012-11-30T13:14:59.943 に答える