2016-01-01 16 views
1

Я знаю Apple has cautioned против использования. Но, учитывая их аргументацию, результаты далеко не актуальны и ожидаются.Почему isKindOfClass и isMemberOfClass плохо работают с NSString и NSMutableString?

Вот мой отладочный вывод - результаты не отличаются в коде - ниже только для краткости:

(lldb) po [@"Hello" isKindOfClass:[NSMutableString class]] 
true => A mutable string? 

(lldb) po [[@"Hello" mutableCopy] isKindOfClass:[NSMutableString class]] 
0x00000001019f3201 => What's that? 

(lldb) po [[@"Hello" mutableCopy] isMemberOfClass:[NSMutableString class]] 
0x000000010214e400 => What's that? 

(lldb) po [@"Hello" isMemberOfClass:[NSMutableString class]] 
false => Once again? 

В дополнение к этому, я удалил все строки буквенный код и протестировали следующий:

NSMutableString * m = [[NSMutableString alloc] initWithString:@"Hello"]; 

bool b = [m isKindOfClass:[NSMutableString class]]; 
NSLog(@"%d", b); --> 1 Expected. 
b = [m isKindOfClass:[NSString class]]; 
NSLog(@"%d", b); --> 1 Expected. 
b = [m isMemberOfClass:[NSString class]]; 
NSLog(@"%d", b); --> 0 Expected. 
b = [m isMemberOfClass:[NSMutableString class]]; 
NSLog(@"%d", b); --> 0 Not Expected. 

Есть ли просвещение?

UPDATE:

компании Apple самостоятельно принять:

Будьте осторожны при использовании этого метода на объектах, представленных в классе кластера. Из-за природы кластеров классов объект, который вы получаете , может не всегда быть типом, который вы ожидали. Если вы вызываете метод , который возвращает кластер классов, точный тип, возвращаемый методом, является лучшим индикатором того, что вы можете сделать с этим объектом.

Почему бы просто не сказать, не используют isKindOfClass и isMemberOfClass с кассетными классами?

Объяснение предотвращает использование с точки зрения, такие как:

Вы могли бы закончить изменения то, что вы не должны.

вместо указания:

Эти методы не работают с кластерами класса. (в примерах, я показал выше - я четко передавая нужные объекты и до сих пор не получают ожидаемых результатов.)

UPDATE 2:

Поданный с Apple Radar.

+1

Добавить 'NSLog (@" m class is% @ ", [m class]);'. Вероятно, вы обнаружите, что это не совсем «NSMutableString», поэтому ваш последний тест терпит неудачу. – rmaddy

+3

BTW - 'isMemberOfClass:' и 'isKindOfClass:' return 'BOOL' поэтому используйте' p', а не 'po' в отладчике для печати значения результата. – rmaddy

+0

Я знаю свой кластер классов - это происходит с NSNumber и NSArray тоже по той же причине. Но я хочу знать, что такое рассуждение Apple за методами передачи, которые вводят в заблуждение. Чтение свойства (класса) может иногда вводить в заблуждение. И это определенно внедорожная вещь, чтобы заменить isKindOfClass только для каких-то объектов. Я очень сомневаюсь, что responseSoSelector является надежным, учитывая приведенные выше результаты? –

ответ

5

Методы не «вводят в заблуждение», как вы утверждаете в комментариях. Поскольку NSString и NSMutableString являются классовыми кластерами, они могут возвращать экземпляр любого конкретного подкласса, который равен -NSString или NSMutableString, соответственно.

Как обычно, большинство подклассов в кластере NSString также являются подклассами NSMutableString. Вместо того, чтобы использовать фактический класс для управления изменчивостью, они используют флаг или что-то в этом роде. Все отлично действует и соответствует контракту на проектирование.

Итак, поэтому [@"Hello" isKindOfClass:[NSMutableString class]] возвращает true. Вы спрашиваете «изменчивая строка?». Нет. Это выражение не является достоверным критерием изменчивости. Как указано, там нет действительный тест на изменчивость.Это лежит в основе вашего недоразумения. Вы не должны пытаться опросить класс объекта, чтобы определить, является ли он изменчивым. Вы должны уважать статический тип указателя в API.


Edit: Это отражено в Concepts in Objective-C Programming: Object Mutability – Receiving Mutable Objects:

Используйте Возвращаемый тип, не интроспекция

Чтобы определить, может ли он изменить принятый объект, приемник , письмо полагаться на формальный тип возвращаемого значения. Если он получает, например, объект массива, типизированный как неизменный, он должен не пытаться его мутировать. Это не является приемлемой практикой программирования , чтобы определить, является ли объект изменяемым на основе его класса членства для Например:

if ([anArray isKindOfClass:[NSMutableArray class]]) { 

    // add, remove objects from anArray 

} 

По причинам, связанным с реализацией, то, что isKindOfClass: возвращается в этом случае не может быть точным. Но по причинам, отличным от этого, вы не должны делать предположения о том, является ли объект изменчивым на основе на членство в классе. Ваше решение должно руководствоваться исключительно тем, что подпись метода, предлагающего объект, говорит о его изменчивости. Если вы не уверены, является ли объект изменчивым или неизменным, предположим, что он неизменен.

Несколько примеров могут помочь разъяснить, почему это руководство является важно:

  • Читаешь список свойств из файла. Когда инфраструктура Foundation обрабатывает список, он замечает, что различные подмножества списка свойств идентичны, поэтому он создает набор объектов, которые разделяет среди всех этих подмножеств. После этого вы просматриваете созданные объекты объектов и решаете мутировать одно подмножество. Внезапно и , не зная об этом, вы изменили дерево в нескольких местах.

  • Вы спрашиваете NSView для его подвидов (с помощью метода subviews) и возвращает объект, который объявляется NSArray, но которая могла бы быть NSMutableArray внутренне. Затем вы передаете этот массив другому коду , который посредством интроспекции определяет его как изменяемый, а изменяет его. Изменяя этот массив, код мутирует внутренние данные структур класса NSView.

Так что не сделать предположение о объектного изменчивости, основанном на том, что самоанализа говорит вам об объекте. Обработать объекты как изменчивые или не на основе того, что вы передаете на границах API (то есть на основе по типу возврата). Если вам нужно однозначно пометить объект как , изменяемый или неизменный, когда вы передаете его клиентам, передайте эту информацию как флаг вместе с объектом.


Как уже упоминалось, -isMemberOfClass: тесты для того экземпляр этого точного класса, а не какой-либо подкласс. Для кластера классов, который всегда будет возвращать false, потому что открытый класс является абстрактным и никогда не будет иметь экземпляры.

Другие странные результаты, вероятно, потому, что вы используете po (сокращение от «объекта печати») для значений, отличных от объекта. Используйте команду p для булевых выражений.

+0

+1 Хорошее объяснение. Таким образом, кластерные классы не должны проверяться с помощью этих методов.Я отредактировал свой вопрос с записью собственного документа Apple - если это что-то иное, чем запутанное, я приглашаю 1000 downvotes. –

+0

Я обновил свой ответ ссылкой и цитатой соответствующей документации, которая не является тем, что вы искали. Обратите внимание, что второй пример действительно не связан с кластерными кластерами или чем-то, что, по-видимому, было выделено как неизменяемое, изменяемое. Просто вам не разрешено мутировать то, что было дано вам с неизменным статическим типом. –

+0

Хорошая точка. Однако одна вещь, которая по-прежнему остается без ответа, - я не могу использовать рефлексию для определения мутируемости, но тип объекта - в этом случае массив. Не могу ли я вернуть функцию id, а затем хочу преобразовать ее обратно в массив с помощью отражения? Конечно, я мог бы использовать методы NSArray + responsesToSelector, чтобы решить, является ли его массив (по крайней мере, если он изменен/не изменен), но он все еще является взломом - альтернативой отражению. Apple должна обновить свою реализацию isMemberOfClass/isKindOfClass или просто отключить методы для кластеров. –

2

tl; dr Не используйте интроспекцию в своем коде для определения изменчивости или изменения поведения (за пределами крайне ограниченных ситуаций). Используйте статические типы и сильно определенные структуры данных (включая сильно определенные структуры plist).

Функциональность интроспекции, предлагаемая средой Objective-C, из которой применяются методы интроспекции на NSObject, не вводят в заблуждение и не возвращают неверные результаты.

Они раскрывают конкретные детали того, как реализуются различные объекты. Это может сильно отличаться от того, что объявлено в файлах заголовков.

Или, иначе говоря: время компиляции против запуска время может быть очень разными.

Это верно и хорошо. И смущает.

Цель-C реализует сильное понятие утка, набирая. То есть, если у вас есть ссылка на что-то, объявленное как NSString*, это действительно не имеет значения, какая эта ссылка действительно указывает на то, что она отвечает на контракт API, объявленный для класса NSString.

Путаница происходит от попытки лечения Objective-C как полностью динамичного, интроспективного, управляемого языка. Он не был предназначен для этого (ну, как бы то ни было, но это понятие было снижено примерно к 1990 году), и со временем сильная типизация стала все более нормой. То есть пусть компилятор выяснит, что-то действительно, и не пытайтесь вторгаться во время выполнения.

 Смежные вопросы

  • Нет связанных вопросов^_^