简体   繁体   中英

Why does [NSMutableString stringWithString:@“”] work?

Just wondering:

In NSString there is a static method called +stringWithString: . This is not redeclared/overridden in NSMutableString so we cannot assume that this will return an NSMutableString . In fact even in the NSString class the return type is defined as id and the doc states :

Return Value
A string created by copying the characters from aString .

What part of objective C am I missing in my knowledge to understand why this works and returns an NSMutableString ? Especially because the base class NSString is not aware that we want a mutable string in return.

One could say that internally [Class alloc] is called which will generate an object of type NSMutableString , but even this is pure guesswork as we do not have the source code and stringWithString: could do whatever it wants internally.

Are all those class methods reimplemented in the subclass? And if yes why isn't this documented?

In NSString there is a static method called +stringWithString:.

more approriately, it's a class method.

This is not redeclared/overridden in NSMutableString

In cocoa, the subclass does not need to redeclare the method. In fact, it would just produce a lot of noise (IMO). It only needs to redefine the method to provide its custom implementation.

so we cannot assume that this will return an NSMutableString.

We must assume that it will return a mutable string. A subclass can redefine its initializers and convenience constructors as needed in order to meet the required contracts without publicly redeclaring the method -- it only needs to define the method when the base's implementation is insufficient.

What part of objective C am I missing in my knowledge to understand why this works and returns an NSMutableString? Especially because the base class NSString is not aware that we want a mutable string in return.

It 'knows' because you have written [NSMutableString stringWithString:@"bah"] rather than [NSString stringWithString:@"bah"] . Like instance methods, class methods have an implicit self which allows them to pass the type through class methods. Therefore, class methods may be redefined/overridden as needed. The class methods may also use self to determine or message their type (example shortly).

One could say that internally [Class alloc] is called which will generate an object of type NSMutableString, but even this is pure guesswork as we do not have the source code and stringWithString: could do whatever it wants internally.

It should not be guesswork. In this case, you should file a bug if you were returned an immutable string. Otherwise, it works as advertised, regardless of whether they used one or more definitions.

Are all those class methods reimplemented in the subclass?

In the case of convenience constructors, it's more common to go through one of the designated initializers:

such an implementation could take the form:

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

although exceptions can often be made, and is often the case with optimized immutable/mutable types:

@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];
}
...

And if yes why isn't this documented?

They should not be redeclared or redocumented when the only difference is the class type which you have requested, unless they have some deviation from the base class or special note -- even in that case, it would be better to create a method with another name.

NSMutableString is a subclass of NSString thus any method called on NSMutableString can be expected to work appropriately, and return an NSMutableString when it makes sense. The only method that comes to mind that doesn't follow that though is copy which by convention returns an immutable instance.

This is why your initializer methods return id rather than a concrete instance, and why all class methods should use [self alloc] instead of [MYActualClass alloc] .

You can write the initializer a bit like this:

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

Now when you call this initializer on NSString , [self alloc] returns NSString and you get an instance of NSString back. But when you call [NSMutableString stringWithString:@"…"] , the result of the [self alloc] message is NSMutableString , and therefore the initializer returns a mutable string.

(What Justin said, but a clarification)

In NSString there is a static method called +stringWithString:. This is not redeclared/overridden in NSMutableString so we cannot assume that this will return an NSMutableString. In fact even in the NSString class the return type is defined as id ...

First and foremost, Objective-C does not have static methods . Objective-C has class methods . Class methods behave exactly like instance methods (where the class is an instance of a metaclass) and can be inherited, overridden, etc...

Thus, exactly as NSMutableString inherits characterAtIndex: and can override it to do something special, if needed, NSMutableString can do the same for class methods.

Note also that there is no need for a class to declare a method in the header that it overrides from its superclass. The classes in the framework generally do not declare overridden methods explicitly because the overridden method should behave exactly as the parent, but is overridden to work within the context of the subclass's requirements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM