Использование класса как ключа в NSDictionary
Я пишу контекстный "factory", который будет поддерживать словарь преобразователей/действующих объектов, которые наследуются от некоторого класса конвертера. Этот класс имеет метод:
- (Class)classResponsibility
Или что-то подобное, так что класс StringConverter реализует метод как:
- (Class)classResponsibility {
return [NSString class];
}
Затем, чтобы сохранить этот конвертер в словаре, я надеялся сделать что-то вроде:
[converters setValue:stringConverter forKey:[stringConverter classResponsibility]];
Но компилятор жалуется, что тип "Класс" является недопустимым типом параметра для аргумента 2 метода setValue: forKey:. Я не хотел ставить ключ как имя класса ( "NSString" ), но если это лучшее решение, чем я поеду с ним.
Ответы
Ответ 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,
};