Objective-C Associated Object
9643 ワード
この文書は実際に2015/08/03に作成された.
Objective-Cは、既存のクラスに属性を追加する方法を提供します.すなわちAssociated Object.これはiOS 4が導入した新しい特性である.既存のクラスにメソッドを追加したり、プロパティを追加したりすることは、既存のクラスを強化するシーンで非常に強力です.しかし、競合するプロパティが追加されるなど、実行時に混乱することがあることに注意してください.あるいは利用者を混乱させ、どちらが強化された属性や方法なのか.参照先:http://nshipster.com/associated-objects/
NSObjectクラスにauthorプロパティを追加します.
コードは次のとおりです.
使用するときはいつものようにしてください.
Since SELs are guaranteed to be unique and constant, you can use _cmd as the key for objc_setAssociatedObject(). #objective-c#snowleopard
— Bill Bumgarner (@bbum) August 28, 2009
Associative Object Behaviors
関連付けられた値の最後のパラメータを
これらのポリシー・オプションは、
さらに進む
コードの実装を次に示します.
私たちの上のコード
大きなデータ構造は、Objective-Cの運転時、グローバルなobject->O b jectAssociationMapのAssociationsHashMapという名前のHashテーブルを維持します. 、ObjectAssociationMapは、あるオブジェクトに属するKey対ValueのHashマッピングテーブルである. は、Valueの記憶も記録する参照処理ポリシーであり、ObjcAssociationを用いてValue. をパッケージする.
保存で指定したPolicyは、値を保存するときや値を取るときに使用されます.主に次のような使用があります.
Policyのビットフラグ宣言:
比較:
バイナリ表現:
Objective-Cは、既存のクラスに属性を追加する方法を提供します.すなわちAssociated Object.これはiOS 4が導入した新しい特性である.既存のクラスにメソッドを追加したり、プロパティを追加したりすることは、既存のクラスを強化するシーンで非常に強力です.しかし、競合するプロパティが追加されるなど、実行時に混乱することがあることに注意してください.あるいは利用者を混乱させ、どちらが強化された属性や方法なのか.参照先:http://nshipster.com/associated-objects/
NSObjectクラスにauthorプロパティを追加します.
コードは次のとおりです.
@interface NSObject (MyObject)
@property (nonatomic,strong) NSString * author;
@end
@implementation NSObject (MyObject)
- (NSString*)author{
return objc_getAssociatedObject(self, "author");
}
- (void )setAuthor:(NSString*)author{
objc_setAssociatedObject(self, "author", author, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
使用するときはいつものようにしてください.
NSObject * myObject = [[NSObject alloc] init];
myObject.author = @"banxi";
NSLog(@"myObject author is %@
",myObject.author);
objc_getAssociatedObject(self, "author");
という値の取り方は、Objective-CのオブジェクトごとにKey-Valueコンテナが付いているように感じられます.上の容器はself
、keyは"author"
ですが、ここではKeyは文字列ではありません.そのタイプ宣言はconst void *key
なので、setterとgetterが一致すればよい.一般的に推奨される方法は、keyがstatic char
タイプ、またはポインタであることを宣言することです.基本的に満たすのは定数で、唯一で、そのsetter、getterはアクセスできます.良い実装はselector
を使用することですSince SELs are guaranteed to be unique and constant, you can use _cmd as the key for objc_setAssociatedObject(). #objective-c#snowleopard
— Bill Bumgarner (@bbum) August 28, 2009
Associative Object Behaviors
関連付けられた値の最後のパラメータを
objc_setAssociatedObject(self, "author", author, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
に格納し、値を格納するときにいくつかの属性関連オプションを指定します.次のようになります.enum {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
これらのポリシー・オプションは、
@property
で宣言されたものと同等です.ASSIGN -> `@property (assign) or @property (unsafe_unretained)`
RETAIN_NONATOMIC -> `@property (nonatomic,strong)`
COPY_NONATOMIC -> `@property (nonatomic,copy)`
RETAIN -> `@property (atomic, strong)`
COPY -> `@property (atomic,copy)`
さらに進む
コードの実装を次に示します.
// 1)
void
objc_setAssociatedObject(id object, const void *key, id value,
objc_AssociationPolicy policy)
{
objc_setAssociatedObject_non_gc(object, key, value, policy);
}
// 2)
void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
// 3)
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
私たちの上のコード
objc_setAssociatedObject(self, "author", author, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
に対して、上の複雑なコードの中で、本当の実行コードは以下の通りです. // retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value){
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
大きなデータ構造は、Objective-Cの運転時、
class ObjcAssociation {
uintptr_t _policy;
id _value;
}
保存で指定したPolicyは、値を保存するときや値を取るときに使用されます.主に次のような使用があります.
// 1)
static id acquireValue(id value, uintptr_t policy) {
switch (policy & 0xFF) {
case OBJC_ASSOCIATION_SETTER_RETAIN:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
case OBJC_ASSOCIATION_SETTER_COPY:
return ((id(*)(id, SEL))objc_msgSend)(value, SEL_copy);
}
return value;
}
// 2)
static void releaseValue(id value, uintptr_t policy) {
if (policy & OBJC_ASSOCIATION_SETTER_RETAIN) {
((id(*)(id, SEL))objc_msgSend)(value, SEL_release);
}
}
// 3) Hash
if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) ((id(*)(id, SEL))objc_msgSend)(value, SEL_retain);
// 4)
if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
((id(*)(id, SEL))objc_msgSend)(value, SEL_autorelease);
}
Policyのビットフラグ宣言:
// expanded policy bits.
enum {
OBJC_ASSOCIATION_SETTER_ASSIGN = 0,
OBJC_ASSOCIATION_SETTER_RETAIN = 1,
OBJC_ASSOCIATION_SETTER_COPY = 3, // NOTE: both bits are set, so we can simply test 1 bit in releaseValue below.
OBJC_ASSOCIATION_GETTER_READ = (0 << 8),
OBJC_ASSOCIATION_GETTER_RETAIN = (1 << 8),
OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8)
};
比較:
enum {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
バイナリ表現:
policy
COPY 1403 10101111011
RETAIN 1401 10101111001
COPY_NONATOMIC 3 00000000011
RETAIN_NONATOMIC 1 00000000001
ASSIGN 0 00000000000
---------------------------------------
SETTER_ASSIGN 0 00000000000
SETTER_RETAIN 1 00000000001
SETTER_COPY 3 00000000011
GETTER_READ (0 << 8) 00000000000
GETTER_RETAIN (1 << 8) 00100000000
GETTER_AUTORELEASE(2 << 8) 01000000000