Почему NSString отвечает на appendString? - PullRequest
15 голосов
/ 05 мая 2011

Я играл с методом responsedsToSelector в Objective-C на MacOS-X 10.6.7 и Xcode 4.0.2, чтобы определить, будет ли объект отвечать на определенные сообщения.Согласно руководствам, NSString не должен отвечать на appendString: тогда как NSMutableString должен.Вот фрагмент кода, который его тестирует:

int main (int argc, const char * argv[])
{

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    NSString *myString = [[NSString alloc] init];

    if ([myString respondsToSelector:@selector(appendString:)]) {
        NSLog(@"myString responds to appendString:");
    } else {
        NSLog(@"myString doesn't respond to appendString:");
    }

    // do stuff with myString

    [myString release];
    [pool drain];
    return 0;
}

, а вот вывод:

Class02[10241:903] myString responds to appendString:

Я бы ожидал обратного.Как объект NSString отвечает на appendString:?Что здесь происходит, что мне не хватает?

Ответы [ 3 ]

10 голосов
/ 05 мая 2011

Краткий ответ: эта строка имеет тип NSCFString, класс, который наследуется от NSMutableString, следовательно, она отвечает на селекторы для методов, объявленных в NSMutableString, включая суперклассы.

Не таккраткий ответ: базовые строки бесплатны и соединены с базовыми базовыми строками.Разработчики используют непрозрачные типы CFStringRef (соединенные NSString) и CFMutableStringRef (соединенные NSMutableString), чтобы ссылаться на эти строки, поэтому, на первый взгляд, есть два различных типа строк: неизменяемые и изменяемые.

С точки зрения внутренней реализации Core Foundation существует закрытый тип, называемый struct __CFString.Этот закрытый тип содержит битовое поле, которое хранит, помимо прочего, информацию о том, является ли строка изменяемой или неизменной.Наличие единственного типа упрощает реализацию, поскольку многие функции совместно используются как неизменяемыми, так и изменяемыми строками.

Каждый раз, когда вызывается функция Core Foundation, которая работает с изменяемыми строками, она сначала читает это битовое поле и проверяет, является ли строка изменяемойили неизменный.Если аргумент должен быть изменяемой строкой, но на самом деле это не так, функция возвращает ошибку (например, _CFStringErrNotMutable) или не подтверждает утверждение (например, __CFAssertIsStringAndMutable(cf)).

В любом случае,это детали реализации, и они могут измениться в будущем.Тот факт, что NSString не объявляет -appendString:, не означает, что каждый экземпляр NSString не отвечает на соответствующий селектор - подумайте заменяемость .Та же самая ситуация применяется к другим изменяемым / неизменным классам, таким как NSArray и NSMutableArray.С точки зрения разработчика, важно то, что возвращаемый объект имеет тип, который соответствует возвращаемому типу - это может быть сам тип или любой подтип этого типа.Кластеры классов делают это немного более запутанным, но ситуация не ограничивается кластерами классов как таковыми.

Таким образом, вы можете только ожидать, что метод возвращает объект, тип которого принадлежит иерархии (т. Е. Либовведите сам или подтип) типа для возвращаемого значения.К сожалению, это означает, что вы не можете проверить, является ли объект Foundation изменчивым или нет.Но опять же, вам действительно нужен этот чек?

Вы можете использовать функцию CFShowStr() для получения информации из строки.В примере в вашем вопросе добавьте

CFShowStr((CFStringRef)myString);

. Вы должны получить вывод, подобный следующему:

Length 0
IsEightBit 1
HasLengthByte 0
HasNullByte 1
InlineContents 0
Allocator SystemDefault
Mutable 0
Contents 0x0

, где

Mutable 0

означает, что строка находится вфакт неизменен.

2 голосов
/ 05 мая 2011

Это, вероятно, связано с реализацией. NSString - это кластер классов, что означает, что NSString - это просто открытый интерфейс, а фактический реализующий класс отличается ( посмотрите, что сообщение class дает вам ).

И в то же время NSString также имеет бесплатный звонок с CFString, что означает, что вы можете свободно переключаться между этими двумя типами, просто приведя:

NSString *one = @"foo";
CFStringRef two = (CFStringRef)one; // valid cast

Когда вы создаете новую строку, вы действительно получаете обратно NSCFString, тонкую оболочку вокруг CFString. И дело в том, что когда вы создаете новую изменяемую строку, вы также получаете экземпляр NSCFString.

Class one = [[NSString string] class]; // NSCFString
Class two = [[NSMutableString string] class]; // NSCFString

Я думаю, это было удобно с точки зрения реализации - и NSString, и NSMutableString могут быть поддержаны общим классом (= меньше дублирования кода), и этот класс гарантирует, что вы не нарушите неизменность:

// “Attempt to mutate immutable object with appendString:”
[[NSString string] appendString:@"foo"];

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

1 голос
/ 05 мая 2011

Вы не должны делать предположения о том, что метод не там.Этот метод может быть использован внутри или по какой-либо причине он существует.Технически, это просто частный API.

У вас есть только контракт с публичными декларациями ( docs ), и они не показывают это сообщение.Так что будьте готовы быстро попасть в неприятности, если будете использовать другие функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...