2010-06-07 15 views
27

Voglio avere un NSDictionary che mappa da UIView s a qualcos'altro.UIView come chiave del dizionario?

Tuttavia, poiché UIViews non implementa il protocollo NSCopying, non posso utilizzarli direttamente come tasti del dizionario.

+1

Rilevante: http://stackoverflow.com/questions/1497622/nsmanagedobject-as-nsdictionary-key – kennytm

+0

Questa sembra davvero una pessima idea. –

+0

Solo se non si è consapevoli del fatto che i dati potrebbero diventare inutili (come indicato dalla risposta accettata). – mrueg

risposta

30

È possibile utilizzare uno NSValue tenendo il puntatore allo UIView e utilizzare questo come chiave. sono copiabili. ma, se la vista viene distrutta, lo NSValue manterrà un puntatore di posta indesiderata .

+0

Favoloso! Stavo per usare un NSNumber con l'hash di UIView come int. Questo è molto più bello, penso. –

+7

Ora con ARC devi usare 'valueWithNonretainedObject'. –

+0

@ Yar Ecco il codice reale utilizzando i suggerimenti di luvieere e te stesso: http://stackoverflow.com/a/11387017/974531 –

17

Ecco il codice vero e proprio (in base a the answer by luvieere e l'ulteriore suggerimento di Yar):

// create dictionary 
NSMutableDictionary* dict = [NSMutableDictionary new]; 
// set value 
UIView* view = [UILabel new]; 
dict[[NSValue valueWithNonretainedObject:view]] = @"foo"; 
// get value 
NSString* foo = dict[[NSValue valueWithNonretainedObject:view]]; 
+0

Come posso verificare se la vista è stata deallata o no? –

4

Anche se questo non è davvero quello che stanno destinati, si potrebbe improvvisare un'interfaccia funzionale simile ai dizionari utilizzando Associative References:

static char associate_key; 
void setValueForUIView(UIView * view, id val){ 
    objc_setAssociatedObject(view, &associate_key, val, OBJC_ASSOCIATION_RETAIN); 
} 

id valueForUIView(UIView * view){ 
    return objc_getAssociatedObject(view, &associate_key); 
} 

si potrebbe anche avvolgere questo in una classe ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable *; in tal caso potresti voler mantenere le viste che usi come chiavi.

Qualcosa di simile (non testata):

#import "ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable.h" 
#import <objc/runtime.h> 

static char associate_key; 

@implementation ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable 

- (void)setObject: (id)obj forKey: (id)key 
{ 
    // Remove association and release key if obj is nil but something was 
    // previously set 
    if(!obj){ 
     if([self objectForKey:key]){ 
      objc_setAssociatedObject(key, &associate_key, nil, OBJC_ASSOCIATION_RETAIN); 
      [key release]; 

     } 
     return; 
    } 

    [key retain]; 
    // retain/release for obj is handled by associated objects functions 
    objc_setAssociatedObject(key, &associate_key, obj, OBJC_ASSOCIATION_RETAIN); 
} 

- (id)objectForKey: (id)key 
{ 
    return objc_getAssociatedObject(key, &associate_key); 
} 

@end 

* Il nome potrebbe essere necessario un certo lavoro.

1

Anziché memorizzare un puntatore alla vista e rischiare il problema dei rifiuti, basta dare a UIView un tag e memorizzare il valore del tag nel dizionario. Molto più sicuro

0

Sto usando una soluzione semplice sotto ARC fornita da Objective-C++.

MyClass.mm:

#import <map> 

@implementation MyClass 
{ 
    std::map<UIView* __weak, UIColor* __strong> viewMap; 
} 

- (void) someMethod 
{ 
    viewMap[self.someView] = [UIColor redColor]; 
} 

In questo esempio sto diventando più forte il controllo di tipo, rendendo tutti i valori deve essere un UIColor* che è tutto quello che ho bisogno di questo per. Ma si potrebbe anche usare id come il tipo di valore, se si vuole consentire a qualsiasi oggetto come il valore, es: std::map<UIView* __weak, id __strong> viewMap; Allo stesso modo per le chiavi: id __weak, id __strong> viewMap;

È anche possibile variare gli attributi __strong e __weak come necessario. Nel mio caso, le viste sono già state mantenute dal controller della vista che io uso in questo, quindi non ho visto alcun bisogno di prendere un puntatore forte a loro.

+1

NSMapTable fornisce anche questo. c.f. '[NSMapTable weakToStrongObjectsMapTable]' – nielsbot

4

A condizione che non sia necessario il supporto prima di iOS 6, NSMapTable (suggerito da neilsbot) funziona bene perché può fornire un enumeratore sulle chiavi della raccolta. È utile per il codice comune a tutti i campi di testo, ad esempio l'impostazione del delegato o la sincronizzazione bidirezionale dei valori di testo con un'istanza NSUserDefaults.

in viewDidLoad

self.userDefFromTextField = [NSMapTable weakToStrongObjectsMapTable]; 
[self.userDefFromTextField setObject:@"fooUserDefKey" forKey:self.textFieldFoo]; 
[self.userDefFromTextField setObject:@"barUserDefKey" forKey:self.textFieldBar]; 
// skipped for clarity: more text fields 

NSEnumerator *textFieldEnumerator = [self.userDefFromTextField keyEnumerator]; 
UITextField *textField; 
while (textField = [textFieldEnumerator nextObject]) { 
    textField.delegate = self; 
} 

in viewWillAppear:

NSEnumerator *keyEnumerator = [self.userDefFromTextField keyEnumerator]; 
UITextField *textField; 
while (textField = [keyEnumerator nextObject]) { 
    textField.text = [self.userDefaults stringForKey:[self.textFields objectForKey:textField]]; 
} 

in textField: shouldChangeCharactersInRange: ReplacementString:

NSString *resultingText = [textField.text stringByReplacingCharactersInRange:range withString:string]; 
if(resultingText.length == 0) return YES; 

NSString *preferenceKey = [self.textFields objectForKey:textField]; 
if(preferenceKey) [self.userDefaults setString:resultingText forKey:preferenceKey]; 
return YES; 

E ora andrò piangere, perché ho realizzato tutto questo prima di rendersi conto che la mia app con iOS 5.1 non può usarla. NSMapTable è stato introdotto in iOS 6.

+0

Questa è un'ottima soluzione! –

0

una soluzione semplice quando si desidera solo UIView come chiave di tanto in tanto, lo uso per archiviare UILabel e UIColor

NSArray<UIView *> *views = @[viewA,viewB,viewC,viewD]; 
NSArray *values = @[valueA,valueB,valueC,valueD]; 

for(int i = 0;i < 4;i++) { 
    UIView *key = views[i]; 
    id value = values[i] 
    //do something 
} 

id value = values[[views indexOfObject:key]] 
Problemi correlati