Вопрос по objective-c – Безопасно ли использовать isKindOfClass: против экземпляра NSString для определения типа?

35

ОтisKindOfClass: документация по методу в NSObject:

Be careful when using this method on objects represented by a class cluster. Because of the nature of class clusters, the object you get back may not always be the type you expected.

Затем документация приводит пример того, почему вы никогда не должны спрашивать что-то вроде следующего экземпляра NSArray:

// DO NOT DO THIS!
if ([myArray isKindOfClass:[NSMutableArray class]])
{
    // Modify the object
}

Теперь, чтобы привести пример другого использования, скажем, у меня есть экземпляр NSObject, где я хотел бы определить, есть ли у меня NSString или NSArray.

Оба эти типа являются кластерами классов - но из документации выше видно, что опасность заключается в ответе на isKindOfClass: слишком утвердительно (отвечая ДА, когда у вас действительно нет изменяемого массива), тогда как задается вопрос о простом членстве в кластер все еще будет действительным.

Пример:

NSObject *originalValue;

// originalValue gets set to some instance

if ( [originalValue isKindOfClass:[NSString class]] )
   // Do something with string

Это предположение верно? Действительно ли безопасно использовать isKindOfClass: против экземпляров кластера классов для определения членства? Меня особенно интересует ответ для вездесущих NSString, NSArray и NSDictionary, но мне было бы интересно узнать, можно ли его обобщить.

Были ли у вас какие-либо идеи об этом за 3 прошедших года? Что за глупостьNSMutableArray подкласс НЕ будет изменчивым? Dan Rosenstark
Из практического опыта это в конечном итоге не имеет значения; как вы говорите, я только когда-либо видел изменчивый NSMutableArrays, и я не думаю, что я когда-либо использовал подкласс (по крайней мере, один, созданный третьей стороной). Большая часть Objective-C посвящена соглашениям, которые не являются явными, и я думаю, вы можете быть уверены, что любой подкласс NSMutableArray, с которым вы можете столкнуться, будет вести себя так, как это имеет смысл для любого другого программиста Objective-C. Однако также следует добавить, что я не сталкивался со случаями, когда мне приходилось делать такую проверку - любой путь с потоком данных должен быть всегда изменяемым или неизменяемым. Kendall Helmstetter Gelner
Согласитесь, это своего рода незначительное беспокойство, которое возникает, когда вы читаете API, но редко в дикой природе. Для меня это подпадает под большую категорию "не писать"if гдеelse состояние не имеет смысла & quot; или что-то типа того. Dan Rosenstark

Ваш Ответ

4   ответа
30

NSMutableArray пример. Предположим, что некоторые разработчики используютNSMutableArray в качестве базового класса для его собственной реализации нового вида массиваCoolFunctioningArray, этоCoolFunctioningArray по дизайну не является изменчивым. Тем не мение,isKindOfClass вернусьYES вisKindOfClass:[NSMutableArray class], что верно, но по дизайну нет.

isKindOfClass: вернусьYES если получатель где-то наследует от класса, переданного в качестве аргумента.isMemberOfClass: вернусьYES только если получатель является экземпляром класса, переданного только в качестве аргумента, то есть не включая подклассы.

В общем-тоisKindOfClass: не безопасно использовать для проверки на членство. Если экземпляр возвращаетсяYES заisKindOfClass:[NSString class], вы знаете только, что он будет реагировать на все методы, определенные вNSString класс, но вы не будете знать наверняка, что может сделать реализация этих методов. Кто-то, возможно, подклассNSString поднять исключение для метода длины.

Я думаю, что вы могли бы использоватьisKindOfClass: для этого вида тестирования, особенно если вы работаете со своим собственным кодом, который вы (как хороший самарянин) разработали таким образом, чтобы он отвечал так, как мы все ожидаем (например, метод длиныNSString подкласс, чтобы возвратить длину строки, которую это представляет вместо того, чтобы вызвать исключение). Если вы используете много внешних библиотек странных разработчиков (таких как разработчикCoolFunctioningArray, который должен быть застрелен) вы должны использоватьisKindOfClass метод с осторожностью, и желательно использоватьisMemberOfClass: метод (возможно, несколько раз, чтобы проверить членство в группе классов).

Кто-то может сделать это подклассом - что нарушает способ работы класса - но это кто-то будетcrazy, Как вы сказали. ответ newacct - это реальная причина в этом случае.
+1 за различие между isKindOfClass и isMemberOfClass. Мне тоже очень нужен этот ответ.
12

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

Тем не менее, тест, данныйisKindOfClass: определенно все еще в силе. Если[something isKindOfClass:[NSMutableArray class]]то это примерNSMutableArray или его подкласс, который в значительной степени означает, что он изменчив.

Это должен быть принятый ответ!
15

if ([dict isKindOfClass:[NSMutableDictionary class]])
{
    [(NSMutableDictionary*)dict setObject:@1 forKey:@"1"];
}

Я думаю, что истинная цель примечания Apple, приведенного в этом вопросе, заключается в том, что этот код может легко привести к сбою. И проблема здесь заключается в бесплатном соединении с CoreFoundation. Рассмотрим более подробно 4 варианта:

NSDictionary* dict = [NSDictionary new];
NSMutableDictionary* dict = [NSMutableDictionary new];
CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

Действительно, во ВСЕХ 4 вариантах истинный класс dict будет __NSCFDictionary. И все 4 варианта пройдут тест в первом фрагменте кода. Но в 2 случаях (объявления NSDictionary и CFDictionaryRef) мы потерпим крах с журналом что-то вроде этого:

* Завершение приложения из-за неперехваченного исключения «NSInternalInconsistencyException», причина: «- [__NSCFDictionary setObject: forKey:]: метод мутирования, отправленный в неизменяемый объект»;

Итак, все становится немного яснее. В 2 случаях нам разрешено изменять объект, а в 2 случаях нет. Это зависит от функции создания, и, вероятно, состояние изменчивости отслеживается самим объектом __NSCFDictionary.

Но почему мы используем один и тот же класс для изменяемых и неизменяемых объектов? Возможный ответ - из-за ограничений языка C (а CoreFoundation - это C API, как мы знаем). Какая проблема у нас в C? Объявления типов изменяемого и неизменяемого словаря должны отражать следующее: Тип CFMutableDictionaryRef является подтипом CFDictionaryRef. Но у С нет механизмов для этого. Но мы хотим иметь возможность передавать объект CFMutableDictionary в функцию, ожидающую объект CFDictionary, и желательно, чтобы компилятор не выдавал раздражающие предупреждения. Что мы должны делать?

Давайте посмотрим на следующие объявления типов CoreFoundation:

typedef const struct __CFDictionary * CFDictionaryRef;
typedef struct __CFDictionary * CFMutableDictionaryRef;

Как мы видим здесь, CFDictionary и CFMutableDictionary представлены одним и тем же типом и отличаются только модификатором const. Итак, начнем с неприятностей.

То же самое в целом относится и к NSArray, но ситуация немного сложнее. Когда вы создаете NSArray или NSMutableArray напрямую, вы получите несколько специальных классов (__NSArrayI и __NSArrayM соответственно). Для этого занятия краша не воспроизводятся. Но когда вы создаете CFArray или CFMutableArray, все остается без изменений. Так что будьте осторожны!

NSDictionary* dict = [NSDictionary new]; не пройдет тест. Тем не менее, спасибо за объяснение бригадных вещей
5

«будьте осторожны с этой функцией, потому что, если вы используете ее с другим неработающим кодом, она не будет работать». Но вы можете сказать это с чем угодно.

Если дизайн следует принципу подстановки, который должен, isKindOfClass будет работать. (Википедия: принцип замещения Лискова)

Если какой-то код нарушает принцип, возвращая экземпляр подкласса NSMutableArray, который не отвечает на addObject: и другие методы NSMutableArray, то у этого кода есть проблема ... если только это не небольшой временный взлом. ..

Похожие вопросы