Подтвердить что ты не робот

Использование класса как ключа в NSDictionary

Я пишу контекстный "factory", который будет поддерживать словарь преобразователей/действующих объектов, которые наследуются от некоторого класса конвертера. Этот класс имеет метод:

- (Class)classResponsibility

Или что-то подобное, так что класс StringConverter реализует метод как:

- (Class)classResponsibility {
    return [NSString class];
}

Затем, чтобы сохранить этот конвертер в словаре, я надеялся сделать что-то вроде:

[converters setValue:stringConverter forKey:[stringConverter classResponsibility]];

Но компилятор жалуется, что тип "Класс" является недопустимым типом параметра для аргумента 2 метода setValue: forKey:. Я не хотел ставить ключ как имя класса ( "NSString" ), но если это лучшее решение, чем я поеду с ним.

4b9b3361

Ответ 1

Вы используете setValue:forKey:, который принимает только NSString как ключи. вы должны использовать setObject:forKey:. Объект класса (указатели на объекты класса могут быть переданы как тип Class) - это полноценный объект Objective-C (объект класса является экземпляром его метакласса, и вы можете использовать все методы NSObject на объект класса, читайте больше о метаклассах здесь), поэтому они могут использоваться везде, где используются объекты.

Другим требованием для ключей словаря является то, что они поддерживают копирование (т.е. имеют метод copyWithZone:). Объекты класса поддерживают этот метод? Фактически, класс NSObject определяет метод класса +copyWithZone:, чья documentation явно говорит, что он "позволяет использовать объект класса в качестве ключа к объекту NSDictionary". Я думаю, что ответ на ваш вопрос.

Ответ 2

Другой вариант - использовать [NSValue valueWithNonretainedObject:yourObjectHere] для создания ключа из чего-то другого, кроме строки. У меня возникла аналогичная проблема, и я хотел использовать объект CoreData в качестве ключа и что-то еще как значение. Этот метод NSValue работал идеально, и я считаю, что это оригинальное намерение. Чтобы вернуться к исходному значению, просто вызовите nonretainedObjectValue

Ответ 3

-setValue:forKey: документируется, чтобы взять NSString в качестве второго параметра. Вы должны использовать NSStringFromClass() и NSClassFromString() в качестве адаптеров.

Ответ 4

Я искал метод setObject: forKey: вместо setValue: forKey:. Подпись метода для setObject: forKey: принимает (id) как оба типа параметров и намного лучше подходит.

Ответ 5

В то время как объект Class делает совершенно хороший ключ в NSDictionary, стоит упомянуть NSMapTable, который моделируется после NSDictionary, но обеспечивает большую гибкость в отношении того, какие объекты подходят для использования в качестве ключей и/или значений, документально для поддержки слабых ссылок и произвольных указателей.

Ответ 6

У меня была похожая ситуация с тем же сообщением об ошибке:

[tempDictionary setObject:someDictionary forKey:someClass];

Все, что я сделал, это реализовать протокол NSCopying в someClass:

- (id)copyWithZone:(NSZone *)zone
{
    id copy = [[[self class] allocWithZone:zone] init];
    [copy setId:[self id]];
    [copy setTitle:[self title]];
    return copy;
}

Я думаю, что происходило то, что была сделана копия someClass для использования в качестве ключа, но поскольку мой объект не знал, как копировать себя (исходя из NSObject, он не имеют copyWithZone в суперклассе), который он отклонил.

Одна вещь, которую я нашел с моим подходом, заключается в том, что она использует объект как ключ. Если я уже не создал объект, я постоянно вызываю allKeys или просто перечислил через словарь.

[После этого я вижу, что вы хотите сохранить класс таким, как ключ. Я оставляю это там, потому что я бы сэкономил много времени, если бы нашел свой ответ, когда искал ТАК. Тогда я не нашел ничего подобного.]

Ответ 7

Вы можете использовать классы как NSDictionary такие как:

@{
    (id)[MyClass1 class] : @1,
    (id)[MyClass2 class] : @2,
    (id)[MyClass3 class] : @3,
    (id)[MyClass4 class] : @4,
};

Или даже так:

@{
    MyClass1.self : @1,
    MyClass2.self : @2,
    MyClass3.self : @3,
    MyClass4.self : @4,
};