Runtime実行時:メンバー変数と属性
9435 ワード
Runtime実行時:メンバー変数と属性
この章の主な内容はRuntimeのメンバー変数と属性の処理に集約されます.議論する前に,タイプ符号化という重要な概念を紹介する.
タイプエンコーディング
Objective-C Runtime Programming GuideのType Encodingセクションには、Objective-Cのすべてのタイプのエンコーディングがリストされています.これらのタイプの多くは、アーカイブおよび配布に使用される符号化タイプと同じであることに注意してください.しかし、アーカイブ時に使用できないものもあります.
注意:Objective-Cはlong doubleタイプをサポートしていません.@encode(long double)はdを返し、doubleと同じである.
1つの配列のタイプ符号化は角カッコにあります.配列要素の個数と要素タイプが含まれます.次の例を示します.
float a[] = {1.0, 2.0, 3.0};
NSLog(@"array encoding type: %s", @encode(typeof(a)));
:
array encoding type: [3f]
メンバー変数、属性
Runtimeのメンバー変数と属性に関するデータ構造は多くなく,3つしかなく,いずれも簡単である.しかし、非常に実用的ですが、よく無視される特性、すなわち関連オブジェクトもあります.このセクションで詳しく説明します.
ベースデータ型
Ivar
Ivarはインスタンス変数を表すタイプで、実際にはobjc_を指すivar構造体のポインタは、以下のように定義されています.
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE; //
char *ivar_type OBJC2_UNAVAILABLE; //
int ivar_offset OBJC2_UNAVAILABLE; //
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
objc_property_t
objc_property_tは、Objective-C宣言の属性を表すタイプであり、実際にはobjc_を指すproperty構造体のポインタは、次のように定義されます.
typedef struct objc_property *objc_property_t;
objc_property_attribute_t
objc_property_attribute_tは、構造体である属性の特性(attribute)を定義し、以下のように定義する.
typedef struct {
const char *name; //
const char *value; //
} objc_property_attribute_t;
関連オブジェクト(Associated Object)
関連オブジェクトはRuntimeの非常に実用的な特性ですが、無視されやすいかもしれません.
関連オブジェクトはメンバー変数と似ていますが、実行時に追加されます.通常、クラス宣言のヘッダファイルにメンバー変数(Ivar)を配置するか、クラス実装の@implementationの後に配置します.しかし、分類にメンバー変数を追加できないという欠点があります.分類に新しいメンバー変数を追加しようとすると、コンパイラがエラーを報告します.
グローバル変数の使用(さらには乱用)によってこの問題を解決することを望んでいるかもしれません.ただし、これらはIvarではありません.個別のインスタンスに接続されないからです.そのため、この方法はあまり使われません.
Objective-Cはこの問題に対して、関連オブジェクト(Associated Object)という解決策を提供しています.
関連オブジェクトは、与えられたkeyを介してクラスのインスタンスに接続されたObjective-Cオブジェクト(辞書など)として想像できます.ただし、Cインタフェースを使用しているため、keyはvoidポインタ(const void*)です.また、Runtimeがこのオブジェクトのメモリをどのように管理するかを示すメモリ管理ポリシーを指定する必要があります.このメモリ管理ポリシーは、次の値で指定できます.
OBJC_ASSOCIATION_ASSIGN
OBJC_ASSOCIATION_RETAIN_NONATOMIC
OBJC_ASSOCIATION_COPY_NONATOMIC
OBJC_ASSOCIATION_RETAIN
OBJC_ASSOCIATION_COPY
ホストオブジェクトが解放されると、指定したメモリ管理ポリシーに従って関連オブジェクトが処理されます.指定したポリシーがassignの場合、ホストが解放された場合、関連オブジェクトは解放されません.一方、retainまたはcopyを指定すると、ホストが解放されると、関連オブジェクトが解放されます.自動retain/copyかどうかを選択することもできます.これは、複数のスレッドで関連オブジェクトにアクセスするマルチスレッドコードを処理する必要がある場合に便利です.
1つのオブジェクトを他のオブジェクトに接続するには、次の2行のコードが必要です.
static char myKey;
objc_setAssociatedObject(self, &myKey, anObject, OBJC_ASSOCIATION_RETAIN);
この場合、selfオブジェクトは新しい関連オブジェクトanObjectを取得し、メモリ管理ポリシーは自動retain関連オブジェクトであり、selfオブジェクトが解放されると自動的にrelease関連オブジェクトがreleaseされます.また、同じキーを使用して別のオブジェクトを関連付ける場合、以前に関連付けられたオブジェクトも自動的に解放されます.この場合、以前の関連オブジェクトは適切に処理され、新しいオブジェクトはメモリを使用します.
id anObject = objc_getAssociatedObject(self, &myKey);
objc_を使用できますremoveAssociatedObjects関数を使用して関連オブジェクトを除去するか、objc_を使用します.setAssociatedObject関数は、keyで指定した関連オブジェクトをnilに設定します.
次に、関連オブジェクトの使用方法を例として説明します.
Tapジェスチャー操作を任意のUIViewに動的に接続し、必要に応じてクリック後の実際の操作を指定したいと仮定します.このとき,ジェスチャーオブジェクトと操作されたblockオブジェクトを我々のUIViewオブジェクトに関連付けることができる.この任務は二つの部分に分かれている.まず、必要に応じて、ジェスチャー認識オブジェクトを作成し、blockを関連オブジェクトとします.次のコードを示します.
- (void)setTapActionWithBlock:(void (^)(void))block
{
UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kDTActionHandlerTapGestureKey);
if (!gesture)
{
gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
[self addGestureRecognizer:gesture];
objc_setAssociatedObject(self, &kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
}
objc_setAssociatedObject(self, &kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}
このコードは、ジェスチャー認識の関連オブジェクトを検出します.ない場合は、関連付けを作成して作成します.同時に、入力されたブロックオブジェクトを指定したkeyに接続します.blockオブジェクトの関連メモリ管理ポリシーに注意してください.
ジェスチャー認識オブジェクトにはtargetとactionが必要ですので、次に処理方法を定義します.
- (void)__handleActionForTapGesture:(UITapGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateRecognized) { void(^action)(void) = objc_getAssociatedObject(self, &kDTActionHandlerTapBlockKey); if (action) { action(); } } }
ジェスチャー認識対象の状態を検出する必要があります.ジェスチャーが認識されたときに操作を実行する必要があるからです.
上記の例から、関連オブジェクトの使用は複雑ではないことがわかります.クラスの既存の機能を動的に強化できます.この特性を実際の符号化で柔軟に運用することができる.
メンバー変数、属性の操作方法
メンバー変数
メンバー変数アクションには、次の関数が含まれます.
//
const char * ivar_getName ( Ivar v );
//
const char * ivar_getTypeEncoding ( Ivar v );
//
ptrdiff_t ivar_getOffset ( Ivar v );
● ivar_getOffset関数は、タイプidまたは他のオブジェクトタイプのインスタンス変数に対してobject_を呼び出すことができます.getIvarとobject_setIvarは、オフセット量を使用せずにメンバー変数に直接アクセスします.
関連オブジェクト
関連オブジェクト操作関数には、次のものが含まれます.
//
void objc_setAssociatedObject ( id object, const void *key, id value, objc_AssociationPolicy policy );
//
id objc_getAssociatedObject ( id object, const void *key );
//
void objc_removeAssociatedObjects ( id object );
ツールバーの
属性アクション関連関数には、次のものがあります.
//
const char * property_getName ( objc_property_t property );
//
const char * property_getAttributes ( objc_property_t property );
//
char * property_copyAttributeValue ( objc_property_t property, const char *attributeName );
//
objc_property_attribute_t * property_copyAttributeList ( objc_property_t property, unsigned int *outCount );
● property_copyAttributeValue関数、返されるchar*は、使用後にfree()リリースを呼び出す必要があります.
● property_copyAttributeList関数は、使用後にfree()解放を呼び出す値を返します.
まとめ:
前の記事では、属性のコード参照の一部を取得しました.http://southpeak.github.io/blog/2014/10/30/objective-c-runtime-yun-xing-shi-zhi-er-:cheng-yuan-bian-liang-yu-shu-xing/