2012-11-30 22 views
10

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

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

私は新しいリテラル構文に似た何かを使って自分のプロパティを参照することができます。 mySpecialClass["anyProperty"]

CFDictionaryバッキングストアを持たないダイナミックなNSDictionaryのようなものを作成したいと思うのですが、これらのアクセサメソッドに渡されたプロパティ名をそれぞれ取得して設定する2つのカスタムメソッドを実行します何をすべきか。

+0

はい、可能です。 –

+1

(3つの基本的なメカニズムがあります:1)実際にクラスにメソッドを追加する、2) "認識されないメッセージ"エラーを傍受して実装を提供する、3)プロパティに対してセッターとゲッターを傍受する特定の方法があります。しかし、私は手元に細部を持っていません。) –

答えて

32

これを行うには少なくとも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:にアクセスすることができます。これにより、メソッド呼び出しを選択したものにリダイレクトできます。

// 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:を実装します。あなたの質問に答えるために、あなたは見えアップ値を辞書IVARに、一般的なゲッターとセッターを記述する必要があります。

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

はまた、それは、NSInvocationで包み、forwardInvocation:に渡される方法、ためNSMethodSignatureを返しますが、メソッドを追加すると、より高速である可能性があります。

Here is a gist CodeRunnerで実行されます。 myClass["anyProperty"]コールは処理されません。

+5

私はこの答えのawesomenessを指摘したいと思います。 –

+1

コードは、Rob NapierとMugunth Kumarの "iOS 5 Programming Pushing the Limits"からのものです。第20章:Deep Objective-Cでは@dynamicプロパティを実装する方法について説明しています。この本には、そのような高度な技術がいくつかあります。私は本といくつかの場所からの情報でOmnigraffleでグラフィックを作った。 – Jano

+2

+1 ... [Mike Ashの言葉](http://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html)型エンコーディングをハードコードするのではなく、型エンコーディングを調べるほうがよい場合があります。 – wbyoung

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#### 
} 

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

あなたは、実行時にプロパティを作成したい場合はそれ以外の場合は、進行NSObject年代forwardInvocation:方法を見て、または動的にクラスを変更する機能のためにObjective-C Runtime Referenceを見るにはさまざまな方法があります...

1

ギョームが正しい。 forwardInvocation:は道のりです。この答えは、いくつかの詳細を与える:method_missing-like functionality in objective-c (i.e. dynamic delegation at run time)

これはさらに多くの詳細を持っている:Hidden features of Objective-C

をお楽しみ

Equivalent of Ruby method_missing in Objective C/iOS

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

関連する問題