Unikaj dodatkowych zmiennych statycznych dla powiązanych kluczy obiektów
Podczas korzystania z powiązanych obiektów, funkcji runtime Objective - C dostępnej od systemów iOS 4 i OSX 10.6, konieczne jest zdefiniowanie klucza do przechowywania i pobierania obiektu w czasie wykonywania.
Typowe użycie to definiowanie klucza w następujący sposób
static char const * const ObjectTagKey = "ObjectTag";
A następnie użyj jest do przechowywania obiektu
objc_setAssociatedObject(self, ObjectTagKey, newObjectTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
I odzyskać
objc_getAssociatedObject(self, ObjectTagKey);
(Przykład wzięty przez http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/)
Jest jest jakiś sposób na zdefiniowanie powiązanego klucza obiektu, który nie wymaga deklaracji dodatkowych zmiennych?
3 answers
Zgodnie z tym wpisem na blogu Erica Sadun (której kredyty idą do Gwynne Raskind ), jest.
objc_getAssociatedObject
i objc_getAssociatedObject
wymagają klucza do przechowywania obiektu. Taki klucz musi być stałym wskaźnikiem void
. Więc w końcu potrzebujemy tylko stałego adresu, który pozostaje stały w czasie.
Okazuje się, że implementacja @selector
zapewnia dokładnie to, czego potrzebujemy, ponieważ używa stałych adresów.
Możemy więc po prostu pozbyć się kluczowej deklaracji i wystarczy użyć adresu selektora naszego obiektu.
Więc jeśli kojarzysz w runtime właściwość taką jak
@property (nonatomic, retain) id anAssociatedObject;
Możemy zapewnić dynamiczne implementacje dla gettera / settera, które wyglądają jak
- (void)setAnAssociatedObject:(id)newAssociatedObject {
objc_setAssociatedObject(self, @selector(anAssociatedObject), newAssociatedObject, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)anAssociatedObject {
return objc_getAssociatedObject(self, @selector(anAssociatedObject));
}
Bardzo schludne i zdecydowanie czystsze niż zdefiniowanie dodatkowego klucza zmiennej statycznej dla każdego powiązanego obiektu.
Czy to bezpieczne?Ponieważ jest to zależne od implementacji, uzasadnione pytanie brzmi: Czy łatwo się złamie? Cytowanie wpisu na blogu
Apple prawdopodobnie musiałoby zaimplementować zupełnie nowe ABI, aby tak się stało.]}Jeśli weźmiemy te słowa za prawdziwe, to jest to w miarę bezpieczne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-18 20:21:41
Jeśli potrzebujesz dostępu do klucza spoza zakresu pojedynczej metody, dobrym wzorcem dla tego, co prowadzi do bardziej czytelnego kodu, jest utworzenie wskaźnika, który po prostu wskazuje na swój własny adres w stosie. Na przykład:
static void const *MyAssocKey = &MyAssocKey;
Jeśli tylko potrzebujesz dostępu z zakresu jednej metody, możesz po prostu użyć _cmd
, która jest gwarantowana jako unikalna. Na przykład:
objc_setAssociatedObject(obj, _cmd, associatedObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-31 12:53:50
Lekką wariacją na temat omawianego pomysłu @ Gabriele Petronella jest przypisanie słownika do każdego obiektu:
//NSObject+ADDLAssociatedDictionary.h
#import <Foundation/Foundation.h>
@interface NSObject (ADDLAssociatedDictionary)
- (void)addl_setAssociatedObject:(id)object forKey:(id<NSCopying>)key;
- (id)addl_associatedObjectForKey:(id<NSCopying>)key;
@end
//NSObject+ADDLAssociatedDictionary.m
#import <objc/runtime.h>
@interface NSObject (ADDLAssociatedDictionaryInternal)
- (NSMutableDictionary *)addl_associatedDictionary;
@end
@implementation NSObject (ADDLAssociatedDictionary)
- (void)addl_setAssociatedObject:(id)object forKey:(id<NSCopying>)key
{
if (object) {
self.addl_associatedDictionary[key] = object;
} else {
[self.addl_associatedDictionary removeObjectForKey:key];
}
}
- (id)addl_associatedObjectForKey:(id<NSCopying>)key
{
return self.addl_associatedDictionary[key];
}
@end
@implementation NSObject (ADDLAssociatedDictionaryInternal)
const char addl_associatedDictionaryAssociatedObjectKey;
- (NSMutableDictionary *)addl_associatedDictionaryPrimitive
{
return objc_getAssociatedObject(self, &addl_associatedDictionaryAssociatedObjectKey);
}
- (void)addl_setAssociatedDictionaryPrimitive:(NSMutableDictionary *)associatedDictionary
{
objc_setAssociatedObject(self, &addl_associatedDictionaryAssociatedObjectKey, associatedDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)addl_generateAssociatedDictionary
{
NSMutableDictionary *associatedDictionary = [[NSMutableDictionary alloc] init];
[self addl_setAssociatedDictionaryPrimitive:associatedDictionary];
return associatedDictionary;
}
- (NSMutableDictionary *)addl_associatedDictionary
{
NSMutableDictionary *res = nil;
@synchronized(self) {
if (!(res = [self addl_associatedDictionaryPrimitive])) {
res = [self addl_generateAssociatedDictionary];
}
}
return res;
}
@end
Następnie w naszej kategorii na jakiejś podklasie wywodzącej się z NSObject
//Derived+Additions.h
#import "Derived.h"
@interface Derived (Additions)
@property (nonatomic) id anAssociatedObject;
@end
//Derived+Additions.m
#import "NSObject+ADDLAssociatedDictionary.h"
@implementation Derived (Additions)
- (void)setAnAssociatedObject:(id)anAssociatedObject
{
[self addl_setAssociatedObject:anAssociatedObject forKey:NSStringFromSelector(@selector(anAssociatedObject))];
}
- (id)anAssociatedObject
{
return [self addl_associatedObjectForKey:NSStringFromSelector(@selector(anAssociatedObject))];
}
@end
Jedną z zalet skojarzonego podejścia słownikowego jest zwiększona elastyczność, która wynika z możliwości ustawiania obiektów dla kluczy generowanych w czasie wykonywania, nie wspominając o znacznie ładniejszej składni.
A korzyść szczególna w stosowaniu
NSStringFromSelector(@selector(anAssociatedObject))
Czy to NSStringFromSelector
jest gwarantowane, aby dać NSString
reprezentację selektora, który zawsze będzie akceptowalnym kluczem słownikowym. W związku z tym nie musimy się wcale martwić (choć nie sądzę, że jest to uzasadniona troska) o zmiany ABI.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-16 20:52:59