Почему работает [NSMutableString stringWithString:@]?

Просто интересуюсь:

В NSString есть статический метод с именем +stringWithString:. Это не переопределяется/не переопределяется в NSMutableString, поэтому мы не можем предположить, что это вернет NSMutableString. На самом деле, даже в классе NSString тип возвращаемого значения определяется как id, а состояния документа:

Возвращаемое значение
Строка, созданная путем копирования символов из aString.

Какой части цели C мне не хватает, чтобы понять, почему это работает и возвращает NSMutableString? Тем более, что базовый класс NSString не знает, что нам нужна изменяемая строка взамен.

Можно сказать, что внутри вызывается [Class alloc], который сгенерирует объект типа NSMutableString, но даже это чистое предположение, поскольку у нас нет исходного кода, и stringWithString: может делать все, что захочет внутри.

Все ли эти методы класса переопределены в подклассе? И если да, то почему это не задокументировано?


person Joris Mans    schedule 01.12.2011    source источник


Ответы (4)


В NSString есть статический метод +stringWithString:.

точнее, это метод класса.

Это не переопределяется/не переопределяется в NSMutableString.

В какао подклассу не нужно повторно объявлять метод. На самом деле, это просто произвело бы много шума (IMO). Нужно только переопределить метод, чтобы обеспечить его собственную реализацию.

поэтому мы не можем предположить, что это вернет NSMutableString.

Мы должны предположить, что он вернет изменяемую строку. Подкласс может переопределить свои инициализаторы и удобные конструкторы по мере необходимости, чтобы соответствовать требуемым контрактам без публичного повторного объявления метода - ему нужно определить метод только тогда, когда базовой реализации недостаточно.

Какую часть объективного C мне не хватает, чтобы понять, почему это работает и возвращает NSMutableString? Тем более, что базовый класс NSString не знает, что нам нужна изменяемая строка взамен.

Он «знает», потому что вы написали [NSMutableString stringWithString:@"bah"], а не [NSString stringWithString:@"bah"]. Как и методы экземпляра, методы класса имеют неявный self, который позволяет им передавать тип через методы класса. Следовательно, методы класса могут быть переопределены/переопределены по мере необходимости. Методы класса также могут использовать self для определения или сообщения своего типа (пример ниже).

Можно сказать, что внутренне вызывается [Class alloc], который сгенерирует объект типа NSMutableString, но даже это чистое предположение, поскольку у нас нет исходного кода, а stringWithString: может делать все, что захочет внутри.

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

Все ли эти методы класса переопределены в подклассе?

В случае удобных конструкторов чаще используется один из назначенных инициализаторов:

такая реализация может иметь вид:

@implementation NSString
+ (id)stringWithString:(NSString *)arg
{
    // self is either +NSString or +NSMutableString
    return [[[self alloc] initWithString:arg] autorelease];
}

хотя часто могут быть сделаны исключения, и это часто бывает с оптимизированными неизменяемыми/изменяемыми типами:

@implementation NSString
+ (id)stringWithString:(NSString *)arg
{
  return [arg imp_isMutable] ? [[[self alloc] initWithString:arg] autorelease] : arg;
}
...
@implementation NSMutableString
+ (id)stringWithString:(NSString *)arg
{
  return [[[self alloc] initWithString:arg] autorelease];
}
...

И если да, то почему это не задокументировано?

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

person justin    schedule 01.12.2011
comment
Я думаю, вы повторили часть моих предположений (часть о себе), но все сводится к тому, что все методы класса ДОЛЖНЫ быть повторно реализованы подклассом, чтобы возвращать согласованный результат, иначе дерево наследования сломано. Таким образом, хотя технически возможно, что метод базового класса возвращает NSString, потому что он не реализован, этого не должно быть, поскольку это аннулирует предположение, что подкласс является полной реализацией суперкласса с дополнительной функциональностью, и он будет считаться ошибка. Спасибо! - person Joris Mans; 02.12.2011
comment
@Джорис Не совсем; подклассу не нужно переопределять какие-либо методы класса, если методы класса реализованы с использованием типичного шаблона. т.е. если +stringWithString: реализован как return [[[self alloc] initWithString:...] autorelease];, тогда NSMutableString не нужно переопределять этот метод (хотя вместо этого ему нужно будет переопределить метод экземпляра -initWithString:). - person bbum; 02.12.2011
comment
Да, если вы знаете, как реализован базовый класс, и он остается согласованным, вам, очевидно, не нужно его переопределять. В этом есть смысл. - person Joris Mans; 02.12.2011

NSMutableString является подклассом NSString, поэтому можно ожидать, что любой метод, вызванный для NSMutableString, будет работать надлежащим образом и вернет NSMutableString, когда это имеет смысл. Единственный метод, который приходит на ум, который не следует этому, — это copy, который по соглашению возвращает неизменяемый экземпляр.

Вот почему ваши методы инициализации возвращают id, а не конкретный экземпляр, и почему все методы класса должны использовать [self alloc] вместо [MYActualClass alloc].

person Joshua Weinberg    schedule 01.12.2011

(То, что сказал Джастин, но уточнение)

В NSString есть статический метод +stringWithString:. Это не переопределяется/не переопределяется в NSMutableString, поэтому мы не можем предположить, что это вернет NSMutableString. На самом деле даже в классе NSString тип возвращаемого значения определяется как id...

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

Таким образом, точно так же, как NSMutableString наследует characterAtIndex: и может при необходимости переопределить его, чтобы сделать что-то особенное, NSMutableString может сделать то же самое для методов класса.

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

person bbum    schedule 01.12.2011

Вы можете написать инициализатор примерно так:

+ (id) stringWithString: (NSString*) foo
{
    return [[self alloc] initWithString:foo];
}

Теперь, когда вы вызываете этот инициализатор для NSString, [self alloc] возвращает NSString, и вы возвращаете экземпляр NSString. Но когда вы вызываете [NSMutableString stringWithString:@"…"], результатом сообщения [self alloc] будет NSMutableString, поэтому инициализатор возвращает изменяемую строку.

person zoul    schedule 01.12.2011
comment
Вероятно, это приведет к утечке памяти. Имя метода stringWithString: подразумевает, что право собственности на возвращаемый объект не передается получателю. - person Ethan Reesor; 18.11.2012
comment
Я думаю, что это было написано до ARC, так что это действительно была утечка, но сегодня с ARC все работает, как и ожидалось. - person zoul; 18.11.2012
comment
Невозможно добавить вызов autorelease в ARC, код и так использует хорошее управление памятью. - person zoul; 18.11.2012