How can i implement the same behaviour as in cluster pattern by apple (NSString and NSCFString)

114 Views Asked by At

I am simply writing the following code for testing purpose:

NSString *aStr = [[NSString alloc] initWithFormat:@"Foo"];
aStr = [aStr initWithFormat:@"Bar"];//Crashed here

I am getting the following error:

*** initialization method -initWithFormat:locale:arguments: cannot be sent to an abstract object of class __NSCFString: Create a concrete instance!

If i write the following code same thing happen

NSString *aStr = [NSString alloc];
aStr = [aStr initWithFormat:@"Foo"];
aStr = [aStr initWithFormat:@"Bar"]; //Crashed here

By google I come to know that initWithFormat will return the NSCFString Object. My question is if NSCFString is derived class of NSString then why I cannot invoke the initWithFormat method on NSCFString. If it is possible to stop the visibility how can I implement in the code without overriding the method in NSCFString(Derived Class).

In simple word If NSCFString is derived class of NSString then why i cannot call the base class (initWithFormat) method on that?

3

There are 3 best solutions below

1
On

As per Documentation it returns an NSString object initialized by using a given format string as a template into which the remaining argument values are substituted.

And also you need to use like that below :-

NSString *aStr = [[NSString alloc] initWithFormat:@"%@,%@",@"Foo",@"Bar"];
NSLog(@"%@",aStr);
9
On

I believe that what's happening is that the [NSString initWithFormat:] method is noticing that you have not provided any format specifiers so there is no NSString object that needs building, so it's simply returning the constant NSString object you pass in (@"Foo"):

NSString *aStr = [NSString alloc];
aStr = [aStr initWithFormat:@"Foo"];

So aStr is now of type NSCFString. This is the cause of the crash:

aStr = [aStr initWithFormat:@"Bar"]; //Crashed here

However you should never be calling an init method on an existing object, so to correct the crash use:

aStr = [[NSString alloc] initWithFormat:@"Bar"];

and use format specifiers as you may as well just do:

aStr = @"Foo";
aStr = @"Boo";

which is the same thing only clearer, uses less code and has better performance.

0
On

You are asking "why". Besides the answer "calling init twice on the same object is baaaad" which you ignored, NSString is a class cluster.

[NSString alloc] returns a generic object that isn't really usable as a string but expects an init method to be called. Let's think about something obvious: NSString objects are immutable, but the result of [NSString alloc] cannot be immutable, because otherwise it would be impossible to store a value, right?

That init method will actually return a different object that doesn't accept init methods anymore. While [NSString alloc] is a very flexible object that could do anything, after a call to an init method you get an object back that contains a string that can never be modified again.

(Apple may have implemented this differently from what I say or may change their implementation. Nevertheless, they can and will do things that stop you from doing something stupid).

And a warning: Don't even think about subclassing NSString. I can guarantee that you won't get anything cobbled together that works.