0

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?

Saurav Nagpal
  • 1,247
  • 1
  • 10
  • 27
  • I don't believe that `initWithFormat` will return `NSCFString`. Where did you find that out? – trojanfoe Oct 28 '14 at 10:47
  • I checked while debugging it shows me isA variable of class as NSCFString – Saurav Nagpal Oct 28 '14 at 10:50
  • Have you tried providing some formatting specifiers and using the method as it was intended? – trojanfoe Oct 28 '14 at 10:51
  • Can you please provide code snippet what kind of formatting specifiers? – Saurav Nagpal Oct 28 '14 at 10:53
  • `NSString *aStr = [[NSString alloc] initWithFormat:@"Hello %@", @"World"];` – trojanfoe Oct 28 '14 at 10:54
  • yes the same thing the code is crashing in the second line: [aStr initWithFormat:@"Bar"]; – Saurav Nagpal Oct 28 '14 at 10:54
  • 1
    You should not call `initWithFormat:` on an object that is already initialized. Also where is your format? – rckoenes Oct 28 '14 at 10:58
  • Hi @rckoenes Thanks for the reply But i am not asking for good practice or not. I am asking how can a derived class object cannot call the base class method. – Saurav Nagpal Oct 28 '14 at 11:02
  • Because the object return by `initWithFormat:` is not a `NSString`. `NSString` is a [class cluster](https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/ClassCluster.html). Thus an abstract super class and it returns private, concrete subclasses. – rckoenes Oct 28 '14 at 11:07
  • Yes but cluster class is super class. So derived class instance can call the super class method. – Saurav Nagpal Oct 28 '14 at 11:09
  • Yes an abstract super class, thus not al the super class methods have to be implemented in the child classes. Also `NSString` is the super class, the return `NSCFString` is not a child class per say of `NSString` as in not all methods will work and some might throw errors. Which make sense because to code you are writing is not correct and should no be written this way and causes an error to be thrown. – rckoenes Oct 28 '14 at 11:12
  • Insert `NSLog(@"%p:%@", aStr, aStr);` at each line and see what it prints. – Cy-4AH Oct 28 '14 at 11:16
  • @rckoenes i also asked if the same thing i have to implement in a code how can i do that. Because the way you are telling i tried that the method is only in base class and still my app not crashed in case of my own class cluster – Saurav Nagpal Oct 28 '14 at 11:20

3 Answers3

1

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.

trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • Yes but NSCFString is derived class of NSString. As per cluster pattern then why i cannot call the base class method. – Saurav Nagpal Oct 28 '14 at 11:11
  • 2
    @SauravNagpal You can (and do), however it's designed that if you do it throws the exception you are seeing. – trojanfoe Oct 28 '14 at 11:11
  • @ trojanfoe i have to know how can i implement in a code. Should I need to override all the init method of base class? – Saurav Nagpal Oct 28 '14 at 11:21
  • @SauravNagpal Well that's a different question, so ask that separately. – trojanfoe Oct 28 '14 at 11:22
  • initWithFormat doesn't need any additional parameters if the format string doesn't have a % character in it. That's not the problem. Although it is certainly grating to see such a complicated call instead of just writing @"Foo" or @"Bar". – gnasher729 Oct 28 '14 at 11:24
  • @gnasher729 I believe that is the reason for this particular error as `initWithFormat` is performing an optimization by returning the formatting string if there are no format specifiers. – trojanfoe Oct 28 '14 at 11:28
  • @trojanfoe sorry for the confusion but i already mention that in my question initially. How can i implement that – Saurav Nagpal Oct 28 '14 at 11:40
  • @trojanfoe i posted the question at following link:http://stackoverflow.com/questions/26607693/how-can-i-implement-the-behaviour-as-in-cluster-pattern-by-apple-nsstring-and-n – Saurav Nagpal Oct 28 '14 at 11:54
  • @SauravNagpal That looks like the same question to me. You will need to elaborate about what you want. – trojanfoe Oct 28 '14 at 11:56
0

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);
Hussain Shabbir
  • 14,801
  • 5
  • 40
  • 56
  • I have just done for testing purpose. My question is why it is restricting me to call the base class method again question title "NSCFString Instance cannot invoke initWithFormat" – Saurav Nagpal Oct 28 '14 at 10:58
0

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.

gnasher729
  • 51,477
  • 5
  • 75
  • 98