3

I have a problem with [NSString strigWithFormat:format] because it returns an id, and I have a lot of code where I changed a NSString var to an other personal type. But the compiler does not prevent me that there are places where a NSString is going to be set into another type of object.

So I'm writing a category of NSString and I'm goind to replace all my calls to stringWithFormat to myStringWithFormat.

The code is :

@interface NSString (NSStringPerso)
+ (NSString*) myStringWithFormat:(NSString *)format;
@end



@implementation NSString (NSStringPerso)
+ (NSString*) myStringWithFormat:(NSString *)format {
    return (NSString*)[NSString stringWithFormat:format];
}
@end

The compiler tells me that "Format not a string literal and no format arguments".

Do you see any way to make this work ?

Oliver
  • 23,072
  • 33
  • 138
  • 230

3 Answers3

6

NSString includes a method that takes in an argument list from a variadic function. Look at this sample function:

void print (NSString *format, ...) {
    va_list arguments;
    va_start(arguments, format);

    NSString *outout = [[NSString alloc] initWithFormat:format arguments:arguments];
    fputs([output UTF8String], stdout);
    [output release];

    va_end(arguments);
}

Some of that code is irrelevant, but the key line is NSString *output = [[NSString alloc] initWithformat:format arguments:arguments];. That's how you can construct an NSString in a variadic function/method.


In your case, your code should look something like this:

+ (NSString *)myStringWithFormat:(NSString *)format, ... {
    va_list arguments;
    va_start(arguments, format);

    NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arguments];
    va_end(arguments);

    // perform some modifications to formattedString

    return [formattedString autorelease];
}
Itai Ferber
  • 28,308
  • 5
  • 77
  • 83
1

No Objective-C expert here, but the original method signature for stringWithFormat includes ellipses, which allows you to pass in the arguments that are going to be substituted to the placeholders in the format argument.

EDIT: stringWithFormat is a so-called variadic method. Here's a link to an example.

s.m.
  • 7,895
  • 2
  • 38
  • 46
  • @sm : great, but how can I call a variadic method from another one ? I can't extrapolate the example shown to do this... – Oliver Jan 16 '11 at 00:15
  • 1
    Looks like, er, you can't, because stringWithFormat doesn't have an alternate version using va_list. Try wrapping initWithFormat instead, which has one. Also, take a look at this: http://stackoverflow.com/questions/150543/forward-an-invocation-of-a-variadic-function-in-c – s.m. Jan 16 '11 at 00:32
  • That's not true. `NSString` does have a version using `va_list`: `initWithFormat:arguments:`. Take a look in the [NSString Class Reference](http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html) – Itai Ferber Jan 17 '11 at 23:17
  • @itaiferber you're right, somehow I didn't notice it. +1 on your answer. – s.m. Jan 17 '11 at 23:57
1

Thank you for your help. Reading your reference documentations, I found the solution !

This works :

In the .h

@interface NSString (NSStringPerso)
+ (NSString*) strWithFormatPerso:(id)firstObject, ...;
@end

In the .m

@implementation NSString (NSStringPerso)
+ (NSString*) strWithFormatPerso:(id)firstObject, ... {

    NSString* a;

    va_list vl;
    va_start(vl, firstObject);
    a = [[[NSString alloc] initWithFormat:firstObject, vl] autorelease];
    va_end(vl);

    return a;
}
@end
Oliver
  • 23,072
  • 33
  • 138
  • 230
  • Two of Cocoa's rules are “don't send messages to an object you have created but not initialized” and “don't initialize an object twice”. The easiest way to avoid both of these is to never allocate an object separately from initialization. You should allocate and initialize the NSString object in the same message expression. I would move the autorelease up there, too, and do all three in the same line. You may move the declaration of `a` to between `va_start` and `va_end` or leave it where it is and split out its initializer into an assignment; that's up to your personal style. – Peter Hosey Jan 16 '11 at 11:50
  • @Peter Hosey : I've edited my answer. Is this the code you were thinking about ? – Oliver Jan 17 '11 at 21:19