web-dev-qa-db-ja.com

objc_setAssociatedObject()とは何ですか?どのような場合に使用する必要がありますか?

私が引き継いだプロジェクトでは、元の作者はobjc_setAssociatedObject()を使用することを選択しましたが、それが何をするのか、またはなぜ彼らがそれを使用することに決めたのかは完全にはわかりません。

調べてみることにしましたが、残念ながら、ドキュメントはその目的についてあまり説明していません。

objc_setAssociatedObject
指定されたキーと関連付けポリシーを使用して、指定されたオブジェクトに関連付けられた値を設定します。
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy)
パラメーター
object
関連付けのソースオブジェクト。
key
関連付けのキー。
value
オブジェクトのキーキーに関連付ける値。既存の関連付けをクリアするには、nilを渡します。
policy
関連付けのポリシー。可能な値については、「関連オブジェクトの動作」を参照してください。

それでは、この関数は正確に何をし、どのような場合に使用すべきでしょうか?


回答を読んだ後に編集

それでは、次のコードのポイントは何ですか?

Device *device = [self.list objectAtIndex:[indexPath row]];
DeviceViewController *next = [[DeviceViewController alloc] initWithController:self.controller
                                                                            device:device
                                                                               item:self.rootVC.selectedItem];  
    objc_setAssociatedObject(device, &kDeviceControllerKey, next, OBJC_ASSOCIATION_RETAIN);

デバイスがすでにインスタンス変数である場合、デバイスをView Controllerに関連付けるポイントは何ですか?

66
Jasarien

Objective-C Runtime Reference のリファレンスドキュメントから:

Objective-Cランタイム関数objc_setAssociatedObjectを使用して、1つのオブジェクトと別のオブジェクトを関連付けます。この関数は、ソースオブジェクト、キー、値、および関連付けポリシー定数の4つのパラメーターを取ります。キーはvoidポインターです。

  • 各関連付けのキーは一意である必要があります。典型的なパターンは、静的変数を使用することです。
  • ポリシーは、関連付けられたオブジェクトが割り当てられているかどうかを指定します。
    保持またはコピー、および
    関連付けはアトミックに行われます
    非原子的。このパターンは
    の属性と同様
    宣言されたプロパティ(「プロパティ
    宣言属性」)。定数を使用して関係のポリシーを指定します(
    objc_AssociationPolicyおよび
    連想オブジェクトの動作)。

配列と文字列の間の関連付けを確立する

static char overviewKey;



NSArray *array =

    [[NSArray alloc] initWithObjects:@"One", @"Two", @"Three", nil];

// For the purposes of illustration, use initWithFormat: to ensure

// the string can be deallocated

NSString *overview =

    [[NSString alloc] initWithFormat:@"%@", @"First three numbers"];



objc_setAssociatedObject (

    array,

    &overviewKey,

    overview,

    OBJC_ASSOCIATION_RETAIN

);



[overview release];

// (1) overview valid

[array release];

// (2) overview invalid

ポイント1では、OBJC_ASSOCIATION_RETAINポリシーが配列に関連オブジェクトを保持することを指定しているため、文字列の概要は引き続き有効です。ただし、配列の割り当てが解除されると(ポイント2)、概要が解放されるため、この場合も割り当て解除されます。たとえば、概要の値を記録しようとすると、ランタイム例外が生成されます。

33
visakh7

objc_setAssociatedObjectは、各Objective-Cオブジェクトにキー値ストアを追加します。インスタンス変数には反映されず、オブジェクトの追加の状態を保存できます。

メインの実装の外部にあるオブジェクトに属するものを保存したい場合に非常に便利です。主な使用例の1つは、インスタンス変数を追加できないカテゴリです。ここでは、objc_setAssociatedObjectを使用して、追加の変数をselfオブジェクトに添付します。

正しい関連付けポリシーを使用すると、メインオブジェクトの割り当てが解除されるとオブジェクトが解放されます。

58
Nikolai Ruhe

オブジェクトの関連付けのユースケースのリストは次のとおりです。

one:インスタンス変数をカテゴリに追加します。一般的に、この手法は 推奨 に対してですが、ここでは正当な使用の です。変更できないオブジェクトの追加インスタンス変数をシミュレートするとします(サブクラス化せずにオブジェクト自体を変更することについて話します)。 UIImageにタイトルを設定するとしましょう。

// UIImage-Title.h:
@interface UIImage(Title)
@property(nonatomic, copy) NSString *title;
@end 

// UIImage-Title.m:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>

static char titleKey;

@implementation UIImage(Title)
- (NSString *)title
{
    return objc_getAssociatedObject(self, &titleKey);
}

- (void)setTitle:(NSString *)title
{
    objc_setAssociatedObject(self, &titleKey, title, OBJC_ASSOCIATION_COPY);
}
@end

また、 here は、カテゴリに関連付けられたオブジェクトを使用するかなり複雑な(しかし素晴らしい)方法です。基本的に、UIControlにセレクターの代わりにブロックを渡すことができます。


two:KVOとともにインスタンス変数でカバーされていないオブジェクトに状態情報を動的に追加します。

アイデアは、オブジェクトが実行時にのみ(つまり動的に)状態情報を取得するというものです。したがって、この状態情報をインスタンス変数に格納できますが、この情報を実行時にインスタンス化されたオブジェクトにアタッチし、それを他のオブジェクトと動的に関連付けるという事実は、これがオブジェクトの動的な状態。

これを示す1つの優れた例は、 this ライブラリです。このライブラリでは、関連オブジェクトが [〜#〜] kvo [〜#〜] 通知とともに使用されます。ここにコードの抜粋があります(注:このKVO通知は、そのライブラリ内のコードを動作させるために実行する必要はありません。その変更が発生しました):

static char BOOLRevealing;

- (BOOL)isRevealing
{
    return [(NSNumber*)objc_getAssociatedObject(self, &BOOLRevealing) boolValue];
} 

- (void)_setRevealing:(BOOL)revealing
{
    [self willChangeValueForKey:@"isRevealing"];
    objc_setAssociatedObject(self, &BOOLRevealing, 
       [NSNumber numberWithBool:revealing], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self didChangeValueForKey:@"isRevealing"];
}

ボーナス:これを見てみましょう 討論/説明 マット・トンプソン、独創的なAFNetworkingライブラリの作者による関連オブジェクト

25
abbood

修正された質問に答えるには:

デバイスがすでにインスタンス変数である場合、デバイスをView Controllerに関連付けるポイントは何ですか?

これを行う理由はいくつかあります。

  • deviceクラスにはコントローラーインスタンス変数またはプロパティがないため、変更したりサブクラス化することはできません。あなたはソースコードを持っていません。
  • デバイスオブジェクトに関連付けられた2つのコントローラーが必要であり、デバイスクラスを変更またはサブクラス化することはできません。

個人的には、低レベルのObjective-Cランタイム関数を使用する必要はほとんどないと思います。これは私にはコードの匂いのようです。

5
JeremyP