73

私が引き受けたプロジェクトでは、元の作者が使用することを選択しましたがobjc_setAssociatedObject()、それが何をするのか、なぜ彼らがそれを使用することにしたのか、100%明確ではありません。

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

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に関連付けることのポイントは何ですか?

4

4 に答える 4

66

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

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

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

于 2011-05-06T09:44:13.777 に答える
35

Objective-C Runtime Referenceの参照ドキュメントから:

Objective-C ランタイム関数objc_setAssociatedObjectを使用して、あるオブジェクトと別のオブジェクトを関連付けます。この関数は、ソース オブジェクト、キー、値、関連付けポリシー定数の 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)、概要が解放されるため、この場合も割り当てが解除されます。たとえば、overview の値をログに記録しようとすると、実行時例外が生成されます。

于 2011-05-06T09:33:49.973 に答える
27

オブジェクトの関連付けの使用例のリストを次に示します。

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

また、カテゴリに関連付けられたオブジェクトを使用するかなり複雑な (しかしすばらしい) 方法を次に示します。基本的には、セレクターの代わりにブロックUIControl.


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

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

これを示す優れた例の 1 つがこのライブラリです。このライブラリでは、連想オブジェクトがKVO通知で使用されています。コードの抜粋を次に示します (注: この 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 ライブラリの作成者である Mattt Thompson による関連オブジェクトのこのディスカッション/説明をご覧ください。

于 2013-05-01T05:48:43.087 に答える
5

改訂された質問に答えるには:

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

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

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

個人的には、低レベルのObjective-Cランタイム関数を使用する必要があることは非常にまれだと思います。これは私にはコードの臭いのように見えます。

于 2011-05-06T10:31:43.410 に答える