web-dev-qa-db-ja.com

実行時にオブジェクトにプロパティを追加するにはどうすればよいですか?

実行時にObjective Cオブジェクトにプロパティを追加することは可能ですか?

41
cfischer

class_addProperty()を介してクラスに正式なプロパティを追加することができます:

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

最初の2つのパラメーターは、その名のとおりです。 3番目のパラメータはプロパティ属性の配列であり、各プロパティ属性は、Objective-C type encodings for declared properties に続く名前と値のペアです。ドキュメントでは、プロパティ属性のエンコード用にコンマ区切りの文字列についても言及されていることに注意してください。コンマ区切りの文字列の各セグメントは、1つの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, "v@:@");

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

(トリミングされた)出力:

Steve
Jobs

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

62
user557219

文書化されたNSKeyValueCodingプロトコルを here で確認すると、次のメッセージが表示されていることがわかります。

- (id)valueForUndefinedKey:(NSString *)key

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

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

9
Jack

@properties-いいえ(つまり、ドット構文などを使用します)。ただし、関連付けられたオブジェクトを使用してストレージを追加できます: オブジェクト内でobjc_setAssociatedObject/objc_getAssociatedObjectを使用するにはどうすればよいですか

4
hypercrypt