2011-10-19 10 views

答えて

60

それはclass_addProperty()を経由して、クラスに正式なプロパティを追加することが可能です:

BOOL class_addProperty(Class cls, 
    const char *name, 
    const objc_property_attribute_t *attributes, 
    unsigned int attributeCount) 

最初の2つのパラメータは自明です。 3番目のパラメータはプロパティの属性の配列で、各プロパティの属性はdeclared propertiesのObjective-C type encodingsに続く名前と値のペアです。ドキュメントでは、プロパティ属性のエンコーディングのカンマ区切り文字列については引き続き記載されています。カンマで区切られた文字列の各セグメントは、objc_property_attribute_tインスタンスで表されます。さらに、objc_property_attribute_tは、idというタイプの汎用コード@のほかにクラス名を受け入れます。ここで

が動的にすでに_privateNameと呼ばれるインスタンス変数を持つクラスにnameというプロパティを追加し、プログラムの最初のドラフトです:

#include <objc/runtime.h> 
#import <Foundation/Foundation.h> 

@interface SomeClass : NSObject { 
    NSString *_privateName; 
} 
@end 

@implementation SomeClass 
- (id)init { 
    self = [super init]; 
    if (self) _privateName = @"Steve"; 
    return self; 
} 
@end 

NSString *nameGetter(id self, SEL _cmd) { 
    Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName"); 
    return object_getIvar(self, ivar); 
} 

void nameSetter(id self, SEL _cmd, NSString *newName) { 
    Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName"); 
    id oldName = object_getIvar(self, ivar); 
    if (oldName != newName) object_setIvar(self, ivar, [newName copy]); 
} 

int main(void) { 
    @autoreleasepool { 
     objc_property_attribute_t type = { "T", "@\"NSString\"" }; 
     objc_property_attribute_t ownership = { "C", "" }; // C = copy 
     objc_property_attribute_t backingivar = { "V", "_privateName" }; 
     objc_property_attribute_t attrs[] = { type, ownership, backingivar }; 
     class_addProperty([SomeClass class], "name", attrs, 3); 
     class_addMethod([SomeClass class], @selector(name), (IMP)nameGetter, "@@:"); 
     class_addMethod([SomeClass class], @selector(setName:), (IMP)nameSetter, "[email protected]:@"); 

     id o = [SomeClass new]; 
     NSLog(@"%@", [o name]); 
     [o setName:@"Jobs"]; 
     NSLog(@"%@", [o name]); 
    } 
} 

その(トリミング)出力:

Steve 
Jobs 

getterメソッドとsetterメソッドはより注意深く記述する必要がありますが、実行時に正式なプロパティを動的に追加する方法の例としては十分です。

+1

class_addPropertyはtrueを返しますが、class_getInstanceVariableは常にnilを返します。私はivarの名前の代わりにプロパティ名を入れてみましたが、まだ運がありません。どのような問題が起こる可能性がありますか? – Mercurial

+1

@Bavarious、どうやってコンプライアーを騙しましたか?私は[o name]の結果がコンパイルエラー 'セレクタ' name 'のインスタンスメソッドが見つかりませんでした。 –

+0

@HiteshSavaliya(ARCの前に)これはちょうど可能でした。現在では、少なくとも '-name'セレクタを宣言しなければなりません。 – Michael

8

あなたはNSKeyValueCodingプロトコルを見てみる場合は、あなたが呼ばれるメッセージがあることを確認することができ、hereを文書化:

- (id)valueForUndefinedKey:(NSString *)key 

はあなたが指定した未定義のプロパティのカスタム結果を提供するためにそのメソッドをオーバーライドする必要があります。もちろんこれは、あなたのクラスが対応するプロトコルを使用していることを前提としています。

この種のアプローチは、クラス(たとえば、存在しないセレクタ)に未知の動作を提供するために一般的に使用されます。

関連する問題