web-dev-qa-db-ja.com

オブジェクト内でobjc_setAssociatedObject / objc_getAssociatedObjectを使用するにはどうすればよいですか?

カテゴリ実装内でobjc_setAssociatedObject/objc_getAssociatedObjectを使用してシミュレートされたインスタンス変数をセッターメソッドに格納する場合、セッターメソッドで宣言された変数はゲッターメソッドのスコープ外になるため、ゲッターメソッドでキーにアクセスするにはどうすればよいですか?

編集:次のパターンを使用する場合、セッターメソッドとゲッターメソッドの両方でSTRING_KEYを使用できるようにするには、どこでSTRING_KEYを宣言する必要があるかを明確にします。

@interface NSView (simulateVar)
-(void)setSimualtedString:(NSString *)myString;
-(NSString *)simulatedString;
@end

@implementation NSView (simulateVar)

-(void)setSimualtedString: (NSString *)myString
{
    objc_setAssociatedObject(self, &STRING_KEY, myString, OBJC_ASSOCIATION_RETAIN);
}

-(NSString *)simulatedString
{
    return (NSString *)objc_getAssociatedObject(self, &STRING_KEY);
}

@end
65
leo

addressをキーとして使用できるように、静的変数を宣言します。 objc_setAssociatedObjectの呼び出しはvoid *を取り、静的変数のアドレスのみが実際に使用され、メモリを浪費しているNSString ...の内容は使用されません。

追加するだけです:

static char STRING_KEY; // global 0 initialization is fine here, no 
                        // need to change it since the value of the
                        // variable is not used, just the address
62
Brent Priddy

私はこの質問が古いことを知っていますが、完全を期すために、言及する価値のある関連オブジェクトを使用する別の方法があると思います。このソリューションは@selectorしたがって、追加の変数や定数は必要ありません。

@interface NSObject (CategoryWithProperty)

@property (nonatomic, strong) NSObject *property;

@end

@implementation NSObject (CategoryWithProperty)

- (NSObject *)property {
    return objc_getAssociatedObject(self, @selector(property));
}

- (void)setProperty:(NSObject *)value {
    objc_setAssociatedObject(self, @selector(property), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

http://www.tuaw.com/2013/04/10/devjuice-better-objective-c-associated-objects/ に触発された)

38
zlajo

関連ストレージの場合void *キー、私はそれを行うこの方法が好きです:

static void * const kMyAssociatedStorageKey = (void*)&kMyAssociatedStorageKey; 

これにより、実行可能ファイルに別の定数文字列が含まれることが回避され、その値をそれ自体のアドレスに設定することにより、優れた一意性とconst動作が得られます(したがって、キーの値を変更します)、余分な不要なエクスポートシンボルはありません。

22
ipmcc

ソースファイルのトップレベルで静的(コンパイル単位スコープ)変数を宣言します。次のような意味のあるものにするのに役立つかもしれません:

static NSString *MYSimulatedString = @"MYSimulatedString";
16
Nicholas Riley

かなり近いです。完全な例を次に示します。

。h-file

_@interface NSObject (ExampleCategoryWithProperty)

@property (nonatomic, retain) NSArray *laserUnicorns;

@end
_

。m-file

_#import <objc/runtime.h>

static void * LaserUnicornsPropertyKey = &LaserUnicornsPropertyKey;

@implementation NSObject (ExampleCategoryWithProperty)

- (NSArray *)laserUnicorns {
    return objc_getAssociatedObject(self, LaserUnicornsPropertyKey);
}

- (void)setLaserUnicorns:(NSArray *)unicorns {
    objc_setAssociatedObject(self, LaserUnicornsPropertyKey, unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}

@end
_

通常のプロパティのように-ドット表記でアクセス可能

_NSObject *myObject = [NSObject new];
myObject.laserUnicorns = @[@"dipwit", @"dipshit"];
NSLog(@"Laser unicorns: %@", myObject.laserUnicorns);
_

簡単な構文

または、静的ポインターを作成する代わりに@selector(nameOfGetter)を使用できます。どうして? https://stackoverflow.com/a/16020927/202451 を参照してください。例:

_- (NSArray *)laserUnicorns {
    return objc_getAssociatedObject(self, @selector(laserUnicorns));
}

- (void)setLaserUnicorns:(NSArray *)unicorns {
    objc_setAssociatedObject(self, @selector(laserUnicorns), unicorns, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}
_
9
hfossli

受け入れられた答えはすでに正しかったのですが、説明を追加したいと思います。「キー」のポイントは、偶発的な衝突を起こさない一意の(プロセス/プログラムごとの)識別子を取得することです。

キーがNSUIntegerとして宣言されていると想像してください。多くの人が01、または42などの標準値を使用します。特に、ライブラリまたはフレームワークを使用している場合、衝突が発生する可能性があります。

その代わり、巧妙なAppleエンジニアは、変数を宣言してその変数へのポインターを渡すことを意図して、キーがポインターであると宣言するという考えを持っていましたキー:リンカはその変数にアプリケーション内で一意のアドレスを割り当てるため、一意の衝突のない値を取得することが保証されます。

実際、任意の値を渡すことができますが、ポインターは逆参照されません(テストしました)。したがって、(void *)0または(void *)42をキーとして渡すことは機能しますが、それは良い考えではありません(したがって、そうしないでください)。 objc_set/getAssociatedObjectの場合、重要なのは、キーとして渡された値がプロセス/プログラム内で一意であることだけです。

5
DarkDust