How to create constant NSString by concatenating strings in Obj-C? - objective-c

I'm trying to instanciate a constant NSString by concatanating other NSString instances.
Here is what I'm doing in my implementation file :
static NSString *const MY_CONST = #"TEST";
static NSString *const MY_CONCATENATE_CONST = [NSString stringWithFormat:#"STRING %#", MY_CONST];
It leads to the following compilation error : Initializer element is not constant
I suppose this is because stringWithFormat doesn't return a constant NSString, but since there is no other way to concatenate strings in Obj-C, what am I supposed to do ?
Thanks for your help,
Eric.

I thought there must be a way to do this but the best I could do was using a #define directive. For example,
// Define the base url as an NSString
#define BASE_URL #"http://www.milhouse.co.uk/"
// Now the derived strings glued by magic
NSString *const kBaseURL = BASE_URL;
NSString *const kStatusURL = BASE_URL #"status.html";
NSString *const kBalanceURL = BASE_URL #"balance.html";

static const objects value is determined at compile-time so you indeed cannot add any method calls to their initialization. As an alternative you can do the following:
static NSString *const MY_CONST = #"TEST";
static NSString *MY_CONCATENATE_CONST = nil;
if (nil == MY_CONCATENATE_CONST)
MY_CONCATENATE_CONST = [NSString stringWithFormat:#"STRING %#", MY_CONST];

I think you need to step back and think about if the string needs to be defined as a const.
Clearly the string isn't a constant since you are trying to assign a new value to it - and that is not possible since you specifically instructed the compiler to make sure the value wasn't changed by using the const keyword.
If the string resides as a property in a class you could make it a read-only property - i.e. accessor method but no setter method. You would then be able to construct your string as you wish in the class internally while keeping the callers from changing the value.

Related

How to get classname in objective c Like 'NSString'

I want to get the class name of an object as what we are using.
That means now if I write this code
NSString *s = [NSString string];
NSLog(#"%#",[s class]);
The output is __NSCFConstantString
How can I get it as NSString itself ?
Note : NSString is just an example
I know __NSCFConstantString is correct. But my intention is to get like NSString. Is there any way to acheive this?
Give these a try, they'll output NSString. Keep in mind, the second set requires importing the Objective-C runtime header.
#import <objc/runtime.h>
NSString *string = #"I'm a string.";
NSLog(#"%#",NSStringFromClass([string classForCoder]));
NSLog(#"%#",NSStringFromClass([string classForKeyedArchiver]));
NSLog(#"%s",class_getName([string classForCoder]));
NSLog(#"%s",class_getName([string classForKeyedArchiver]));
Now, this won't work in all cases. For example, trying to get the class of NSConstantString, in this manner will output NSString. If you require checking the class name as a string in this way, you probably should reconsider your approach to solving the problem.
NSString is a so-called "class cluster". That means that the init methods will return
an instance of some subclass (such as __NSCFConstantString or __NSCFString).
You will never get an instance with the class equal to NSString.
If your intention is to check whether an object is a NSString or not then
use isKindOfClass:
if ([s isKindOfClass:[NSString class]]) {
// this is a string …
}
Other examples of class clusters are NSNumber, NSDictionary, NSArray
and their mutable variants.
NSLog(#"%#", NSStringFromClass([s class]));

Constant value of NSString representation

I have a PList where I load a couple of rows of data in a dictionary. I want to add the a line like
<key>StandardValue</key>
<string>STANDARDVALUEFORCERTAININSTANCE</string>
Now when I read out the values I get a NSString. How can I get the value of the constant that I previously defined with
#define STANDARDVALUEFORCERTAININSTANCE 123
Is there a way to get the constant representation of a string? So essentially to parse it?
What you want to do isn't exactly possible. The constants created with #define only exist at compile-time, and at run time there is no way to access them by name - they have been converted to the constant value already.
One alternative that might exist is to define a number of methods that return constant values, say in a Constants class. Then, at run time, load the name of the method from the plist and call it using NSSelectorFromString() and performSelector:.
However, a possible issue with this is that for safety with performSelector: you'd have to rewrite all your constants as Objective-C objects (since performSelector: returns type id). That could be quite inconvenient.
Nevertheless, here is an example implementation of the Constants class:
#implementation Constants : NSObject
+ (NSNumber *)someValueForACertainInstance
{
return #123;
}
#end
And example usage:
NSDictionary *infoDotPlist = [[NSBundle mainBundle] infoDictionary];
NSString *selectorName = infoDotPlist[#"StandardValue"];
SEL selector = NSSelectorFromString(selectorName);
NSNumber *result = [Constants performSelector:selector];
And how the selector name would be stored in the info plist:
<key>StandardValue</key>
<string>someValueForACertainInstance</string>
You can't do it this way. I suggest a nice alternative: KVC.
You declare this variable as class instance:
#property (nonatomic,assign) int standardValueForCertainInstance;
Then you get the value with valueForKey:
NSString* key= dict[#"StandardValue"];
int value= [[self valueForKey: key] intValue];

initlializer element is not constant

guys i m getting this basic error "initializer element is not constant"..not able to figure where exactly i failed .below is the code.`
#implementation myfirstflickrappViewController
NSString *const FlickrAPIKey = #"14c39d71001b0fb84d1dacb6049580ec";
NSString *const text = #"hello";
NSString *urlString =
[NSString stringWithFormat:
#"http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%#&tags=%#&per_page=25&format=json&nojsoncallback=1",
FlickrAPIKey, text];
You call a method on NSString (stringWithFormat:) in a place where you are not allowed to do so - namely outside of a method or function. Only constant expressions like string literals are allowed there.
You might put that code in your -init method or the class initializer.

Problem declaring and calling internal metthods

How do I declare and use small helper functions inside my normal methods ?
In on of my objective-c methods I need a function to find an item within a string
-(void) Onlookjson:(id) sender{
NSString * res = [[sender gstring] copy];
persInfoBirth.text = getKeyValue(res, #"Birth");
}
I came up with a normal C type declaration for helper function getKeyvalue like this
NSString * getKeyvalue(NSString * s, NSString * key){
NSString *trm = [[s substringFromIndex:2] substringToIndex:[s length]-3];
NSArray *list = [trm componentsSeparatedByString:#";"];
//....
NSString res;
res = [list objectAtIndex:1];
//...
return res;
}
Example input string in s:
s=#"{ Birth = "1910"; Death = "1936"; }";
Anyway I get an exception "unrecognized selector sent to instance" for any of the two first lines in the helper function
How do I declare helper functions that are just to be used internally and how to call them safely ?
regards
Martin
Is this the real code? Do you get zero errors and warnings from the compiler? You must not ignore compiler warnings and you should turn on the Static Analyser in addition to the standard warnings.
There are many things wrong with the above code, most of which are nothing todo with declaring and calling methods. There is no way the above code could compile so maybe it pasted incorrectly or something..
Anyway.. declaring and using methods. Why are using a c function? Unless you have a good reason why not use Objective-c ? If you do have a good reason to use a C function the your definition should be:-
NSString *getKeyvalue( NSString *s, NSString *key ){
...
}
note the arguments. As NSString instances reside in the heap (not on the stack) you always want to pass pointers to them.
You then need to put the declaration in the header file:-
NSString *getKeyvalue( NSString *s, NSString *key )
EDIT:
In Objective-c there is no distinction between normal methods and helper methods, there is only one kind, and you have aleray written one
- (void)onLookJson:(id)sender { .. }
Taking it apart..
All methods begin with + or –, indicating Class method or Instance method. As you are familiar with C++ i guess you know what this means.
(void) is the return type. ie this method doesn't return a value. If it did it might look like (float) or (NSString *) or (id).
onLookJson: is the method name and the method takes 1 argument. Notice that the ':' is actually part of the name. This method is never is any circumstance just 'onLookJson'. An argument must always follow the :, so a method that doesn't take any arguments must not have one.
Ex
- (NSString *)fullName { .. }
This is an instance method, for example of a Person Class, you would call it like:-
NSString *theName = [aPerson fullName];
So
a method name that takes no
arguments is like 'speak'
a method
name that takes 1 argument is like
'speakTo:'
a method name that takes 2
arguments is like 'speakTo: language:'
a method name that takes 3
arguments is like 'speakTo: language: volume:'
etc.
All that is left is to put in the argument types and names.
Your function definition:
NSString *getKeyvalue( NSString *s, NSString *key ){
would become..
- (NSString *)getValue:(NSString *)s key:(NSString *)key { .. }
again, you need to declare it in the header or you will get a compiler warning.
- (NSString *)getValue:(NSString *)s key:(NSString *)key;

Using a constant NSString as the key for NSUserDefaults

I'm using NSUSerDefaults to store user preferences. I remember reading somewhere that setting the keys as constants is a good idea - and I agree. The following code is what I currently have:
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:polygon.numberOfSides]
forKey:#"polygonNumberOfSides"];
I tried changing this to:
#implementation Controller
NSString const *kPolygonNumberOfSides = #"polygonNumberOfSides";
-(void)savePolygonInfo {
[[NSUserDefaults standardUserDefaults]
setObject:[NSNumber numberWithInt:polygon.numberOfSides]
forKey:kPolygonNumberOfSides];
}
While this does work, it produces "warning: passing argument 1 of 'objectForKey:' discards qualifiers from pointer target type". I'm keen to keep my code free from compiler warnings. How can I fix this warning?
You should use:
NSString * const kPolygonNumberOfSides = #"..."; // const pointer
instead of:
NSString const * kPolygonNumberOfSides = #"..."; // pointer to const
The first is a constant pointer to an NSString object, while the second is a pointer to a constant NSString object.
It is a subtle difference. The compiler warning happens because setObject:forKey: is declared as follows:
- (void)setObject:(id)value forKey:(NSString *)defaultName;
It is expecting the defaultName argument to be of type NSString *. When you instead pass in a pointer to a constant, you've given it something different.
Update: I want to point out that these constants should be defined as static if they are only going to be used from within a single file. I say this because I have run across this problem myself: if you do not declare them as static, then they will exist in the global namespace, and you will not be able to use a variable with the same the same name in another file. see Constants in Objective-C for more information. To explain by example, this is what I currently use for keys that I only need to use in one .m file:
static NSString * const kSomeLabel = #"...";
Don't use const with Objective-C objects, they weren't really designed to use it. NSString objects (among many others) are already immutable by default by virtue of their design, so making them const is useless.
As e.James suggested, you can use an NSString * const, which is a constant pointer to an NSString. This is subtly different from a const NSString * (equivalent to NSString const *), which is a pointer to a constant NSString. Using a NSString * const prevents you from reassigning kPoly to point to a new NSString object.
For access from other classes:
.h
extern NSString * const PolygonNumberOfSidesPrefsKey;
.m
NSString * const PolygonNumberOfSidesPrefsKey = #"PolygonNumberOfSides"
For access only inside current class:
.m
static NSString * const kPolygonNumberOfSidesPrefsKey = #"PolygonNumberOfSides"
I would suggest even making the constant more descriptive. A constant for the number of sides of a polygon could come from anywhere. As a suggestion, how about:
kDefaultsPolygonNumberOfSides;
instead.