UIView в качестве словарного ключа? - PullRequest
27 голосов
/ 07 июня 2010

Я хочу иметь NSDictionary, который сопоставляет UIView с чем-то другим.

Однако, поскольку UIViews не реализует протокол NSCopying, я не могу использовать их непосредственно в качестве словарных ключей.

Ответы [ 7 ]

30 голосов
/ 07 июня 2010

Вы можете использовать NSValue, удерживая указатель на UIView и использовать его в качестве ключа. NSValues являются копируемыми. но если представление будет уничтожено, NSValue будет содержать указатель мусора.

17 голосов
/ 09 июля 2012

Вот фактический код (на основе ответа Лювьера и дальнейшего предложения Яра):

// 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]];
4 голосов
/ 10 декабря 2014

При условии, что вам не нужно поддерживать до iOS 6, NSMapTable (предложенный neilsbot ) работает хорошо, потому что он может предоставить перечислитель для ключей в коллекции.Это удобно для кода, общего для всех текстовых полей, например, для установки делегата или двунаправленной синхронизации текстовых значений с экземпляром NSUserDefaults.

в 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;
}

в viewWillAppear:

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

в textField: shouldChangeCharactersInRange: replaceString:

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;

А теперь я пойдуплакать, потому что я реализовал все это до того, как понял, что мое приложение для iOS 5.1 не может его использовать. NSMapTable был представлен в iOS 6.

4 голосов
/ 09 июля 2012

Хотя это не совсем то, для чего они предназначены, вы можете создать функциональный словарь-интерфейс, используя Ассоциативные ссылки :

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);
}

Вы могли бы даже обернуть это в классе ThingWhatActsLikeADictionaryButWithKeysThatArentCopyable*; в этом случае вам может потребоваться сохранить представления, которые вы используете в качестве ключей.

Примерно так (не проверено):

#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

* Имя может нуждаться в некоторой работе.

1 голос
/ 19 октября 2012

Вместо того, чтобы хранить указатель на представление и рисковать проблемой мусора, просто присвойте UIView тег и сохраните значение тега в словаре. Намного безопаснее.

0 голосов
/ 21 июля 2017

простое решение, когда вы просто хотите UIView в качестве ключа, я использую его для хранения UILabel и 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]]
0 голосов
/ 04 марта 2014

Я использую простое решение под ARC, предоставленное Objective-C ++.

MyClass.mm:

#import <map>

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

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

В этом примере я получаю более строгую проверку типов, поскольку все значения должны быть UIColor*, и это все, для чего мне это нужно. Но вы также можете использовать id в качестве типа значения, если хотите разрешить любой объект в качестве значения, например: std::map<UIView* __weak, id __strong> viewMap; Аналогично для ключей: id __weak, id __strong> viewMap;

Вы также можете изменять атрибуты __strong и __weak по мере необходимости. В моем случае представления уже сохранены контроллером представления, в котором я использую это, поэтому я не видел необходимости указывать на них сильный указатель.

...