getting the "element not a compile-time" constant error - objective-c

I am writing the following piece of code which gives following error
#implementation ViewController
NSArray *myArray = [[NSArray alloc]initWithObjects:#"paul",#"cyrus",#"victor",nil ];
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
}
I am getting the "Initialization element not a compile-time constant" on the line where i have declared the "myArray"..
Note that i am initializing the array in the ".m" file and not the ".h" file.

If you initialize a global or static variable you have to use a compile-time constant. Basically the compiler will have to write that value to the object file. Your code is sending multiple messages, which can only be done at runtime.
If this really has to be a global variable you'll have to initialize it in some method. +initialize often is used for something like this.

as mentioned, you have declared a global variable.
you can use a simple function instead:
static NSArray * NamesArray() {
return [[NSArray alloc]initWithObjects:#"paul",#"cyrus",#"victor",nil];
}
it's probably not what you want, and completely unnecessary to create a global, but you can in fact initialize a global NSArray using a container literal expression if you're compiling your source as Objective-C++:
// requires Objective-C++
static NSArray * const arr = #[#"paul",#"cyrus",#"victor"];
because this array and its elements are not difficult to create, you should:
just use a function
or an ivar if you need to cache and access it often
if it were really expensive to create, then you might favor this form:
// requires Objective-C++
static NSArray * Names() {
static NSArray * const arr = #[#"paul",#"cyrus",#"victor"];
return arr;
}
because it will not be constructed until you need it. global initialization can be very problematic.

Make myArray an ivar and initialize it in the appropriate place, or create a static global in your class and initialize it in the "initialize" class method.

Related

Using an "extern" global before it is properly initialized

I'm trying to determine if there is an elegant solution to this issue.
Say I have a global defined in some header:
Constants.h:
extern NSString *someGlobal;
And then I wish to use this global in some other class:
Foo.m
NSString *localVariable = someGlobal;
This all works just fine if I initialize the global like this:
Constants.m:
NSString *someGlobal = #"Some String Literal";
But lets say I need to initialize the global to something that isn't a compile-time constant. In such cases I typically do this:
Constants.m:
#implementation Constants
+ (void)initialize {
someGlobal = ... // some non-trivial initialization
}
#end
Now I have a potential problem in Foo.m. If no reference has been made to the Constants class when I try to use someGlobal, the result is nil. A workaround is to do:
Foo.m (or in some app startup code):
[Constants class];
That will trigger the initialize method of the Constants class and someGlobal will be properly initialized. As long as this is done before any runtime use of someGlobal, things work fine.
Is there a better way to initialize extern globals with non-compile time constants without the need to call code such as [Constants class] at app startup?
A more idiomatic way in Objective-C is using a singleton instead of multiple globals. Here is how:
#interface Globals
#property (readwrite,nonatomic) NSString *myString;
#property (readwrite,nonatomic) int myInt;
+(Globals*) instance;
#end
+(Globals*) instance {
static dispatch_once_t once;
static Globals *inst;
dispatch_once(&once, ^{
inst = [[Globals alloc] init];
inst.myString = #"Some String Literal";
inst.myInt = 42;
});
return inst;
}
Now you can use your globals like this:
NSLog(#"Global string: %#", [Globals instance].myString);
NSLog(#"Global string: %d", [Globals instance].myInt);
No, there is no better way. Logically, if some piece of code must execute before a variable is intialized, you have to take steps to make sure that happens.
You could arrange the flow of your program's code so as to guarantee that the Constants class get initialized before any other piece of code executes which needs it. For example, by tweaking the order in which things are initialized in your program and following the order of code execution from main() on down to prove to yourself that it works. But short of that (and the safest thing in any case), you would use your technique to force it to be made valid right before you use it.
Like dasblinkenlight's answer, this may not be exactly what you are looking for but it's another approach.
I would make class methods that returns the value you are looking for like this:
+(NSString *)someConstant {
static NSString *constant;
if(constant == nil)
constant = //your initialization here;
return constant;
}
Then where you need to use it just call [Constants someConstant];
Other random thoughts:
A constant that isn't some compile time value isn't really what extern variables are for and this method insures that the variable is initialized every time you use it. The class using the constant has to know about your class anyway or it wouldn't have imported its header file

"Initializer element is not a compile-time constant" why?

I have this code:
- (NSString *) calculate: (uint) position {
static NSArray * localArray = [NSArray arrayWithArray: self.container.objects ];
// some un related code
return obj;
}
The compiler complains saying: "Initializer element is not a compile-time constant". It happened when I added "static" to localArray. But why?
Because [NSArray arrayWithArray: self.container.objects ] isn't a compile-time constant, it's an expression that must be evaluated at runtime. In C and Objective-C, static variables inside functions must be initialized with compile-time constants, whereas C++ and Objective-C++ are more lenient and allow non-compile-time constants.
Either compile your code as Objective-C++, or refactor it into something like this:
static NSArray *localArray = nil;
if (localArray == nil)
localArray = [NSArray arrayWithArray: self.container.objects ];
Which is fairly similar to the code that the compiler would generate under the hood for a static variable initialized with a non-compile-time constant anyways (in actuality, it would use a second global flag indicating if the value was initialized, rather than using a sentinel value like nil here; in this case, we are assuming that localArray will never be nil). You can check out your compiler's disassembly for that if you want.
You just can't initialize a static variable with a non-static value that will be known/modified at runtime.
You should probably do something like this:
static NSArray *localArray = nil;
localArray = ...;
The first instruction will be executed once in your app lifecycle.
The second instruction will be executed every time the calculate: method is called.
Nevertheless, pay attention to the fact that using static variables can lead to buggy behaviors if not done properly so if you feel uneasy with these, you should probably not use them.

Objective-C enum-map

I am a Java programmer and learning objective C at the moment. For the purpose of creating JSON objects I want to translate the following Java-Pseudocode to objective C. However I got a huge amount of problems with that as it seems that everything I want is not available in objective C. My own variant takes up way more space and is way more complicated but as I am just a beginner I would like to know if there is a more simple and short way to achieve that.
Java Code
import java.util.HashMap;
import java.util.Map;
public class Parameters
{
public enum Language {deDE};
protected Language language = null;
// ... other parameters ...
protected static final Map<Language,String> languageToString = new HashMap<>();
static {languageToString.put(Language.deDE,"de-DE");}
Map<String,String> jsonProxy()
{
HashMap<String, String> map = new HashMap<>();
if(language!=null) {map.put("lang", languageToString.get(language));}
return map;
}
}
My Objective-C try
#interface Parameters : NSObject
enum Lang {deDE};
extern NSMutableDictionary *langToString;
- (id) proxyForJson;
extern int test;
#end
#implementation Parameters
enum Lang* lang = nil;
NSMutableDictionary *langToString;
-(CommunicationParameters*) init
{
self = [super init];
return self;
}
+(void) initialize
{
langToString = [[NSMutableDictionary alloc] init];
[langToString setObject:#"de-DE" forKey:[NSNumber numberWithInt:deDE]];
}
-(id) proxyForJson
{
NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init];
if(lang!=nil)
{
[dictionary setObject:[langToString objectForKey:[NSNumber numberWithInt:*lang]] forKey:#"lang"];
}
// does this work? is a mutable dictionary a subclass of dictionary?
return dictionary;
}
It looks a bit like you've tried to translate the Java code line by line, which won't lead to anything but a headache. A number of things I'd like to point out:
Initialiser methods should always return type id (not a superclass).
Initialiser methods that do nothing but return self don't need to be implemented, this is because if no init method is provided for the class, it will go straight to invoking the superclass's init method.
The memory management for your proxyForJson method is wrong. You are returning an object that you own through a method whose name implies no ownership of the returned value. Instead, create the dictionary by using [NSMutableDictionary dictionary] as this result of this method has no owner.
Your class uses global variables that probably shouldn't be global. I'm not entirely sure, but I think it would be better to make lang an instance variable of your class that is set via an argument to an init method, e.g. initWithLanguageID:. Certainly this would be closer to your Java implementation.
Since enums are sequential and start from 0 (unless specifically written otherwise), you could probably avoid using a map entirely and use a simple static array of NSString objects, one for each entry of the Lang enumeration. Better yet, don't use an enum and allow the caller to specify the language as a string. From what I can see, the enumeration doesn't seem to have any other use but to map an integer to a string.

So very lost on initializing const objects

I have tried many times to understand the const keyword, but it just doesn't work out for me.
I want to declare an object that cannot be changed, which is to say, a constant object. For example, in the .h file:
extern MyClass *use_this_object;
and in the .m file:
MyClass *use_this_object;
+ (void) Initialize {
use_this_object = [MyClass new];
}
Now, where can I put a const so that other classes can access use_this_object but not modify it (assuming MyClass is immutable), while the MyClass class can initialize the variable?
Is this even possible? Or should I be using a static method to retrive the constant and not declare it extern at all?
There is no such thing as a "const object" in Objective-C. There are const pointers and there are immutable objects. A const pointer to an immutable object is what you're talking about, but you can't allocate those at run time.
For objects that can be allocated at compile time (and I only know of one, NSString), you can do this:
NSString * const kMyString = #"string";
This is a constant pointer to an (immutable) NSString. You read these things right-to-left.
To create what you want, you need a function or method with an internal static like this:
+ (Something *)something {
static Something *something = nil;
if (! something) {
something = [Something new];
}
return something;
}
This is preferable to using globals anyway for things other than true constants (like strings and integers).
It is up to you to make sure that Something is immutable.
EDIT Just a note about the above code. This is just an example of how to create a static object at runtime. There are many ways to do it with various trade-offs including using +initialize with a file static (which is currently my preferred way to create a singleton). Don't take the above code as the one-and-only-way. It's just the way that is closest to const because no other part of the program can get directly to the pointer.
Iā€™d use the static method, seems much simpler.

How to build a NSArray (or NSMutableArray) of class methods in Objective-C?

I'm trying to build a NSArray of methods in Objective-C.
(What I'm trying to accomplish here is something like the following in C)
typedef (void)(*handler)(int command);
void handleCommandA(void) { ... }
void handleCommandB(void) { ... }
static const handler handler_table[10] = {
handleCommandA, handleCommandB, handleCommandC
};
I have to port this to Objective-C and I don't know how to
build an array of function pointers (in Objective-c world,
class methods) at compile-time.
In Objective-C I have the following.
- (void)handleCommandA { ... }
- (void)handleCommandB { ... }
/* Now how to add above 2 functions into NSArray? */
NSArray *handler_table = [NSArray arrayWithObjects:... ]; /* This doesn't seem to work. */
The problem here is that to bind those functions you must use the selector keyword which returns a SEL type. This is a pointer type whereas NSArray stores objects.
You thus have three options;
Use a regular C-type array
Fold the functions into an NSObject derived class that will call them.
Use a protocol.
The second is likely the nicer and for this you can use the NSValue class to hold the selector results. E.g;
NSValue* selCommandA = [NSValue valueWithPointer:#selector(handleCommandA:)];
NSValue* selCommandB = [NSValue valueWithPointer:#selector(handleCommandB:)];
NSArray *handler_table = [NSArray arrayWithObjects:selCommandA, selCommandB, nil ];
When you have retrieved the correct entry from the array, to convert back you would do;
SEL mySelector = [selCommand pointerValue];
[someObject performSelector:mySelector];
(Note I'm assuming that from your objective-c syntax that these are intended to be used as methods on an object and not global functions. If you wish to use them globally then you should write them as you would in plain C.)
Another option is to formalize the command methods into a protocol. This allows you to write functionality that will work on any object which implements that protocol and the compiler will provide more checking than if you were just calling selectors.
E.g.
// some header
#protocol CommandHandler
#required
-(void) handleCommandA;
-(void) handleCommandB;
#end
// some other header
#interface someClass : NSObject<CommandHandler>
{
// you will receive compiler warnings if you do not implement the protocol functions
}
Your handling and dispatch code is then written to work with objects of type "CommandHandler". E.g
-(void) registerForCommands:(CommandHandler*)handler
Use NSValue.
For example:
NSArray* handlers = [NSArray arrayWithObjects:[NSValue valueWithPointer:handleA] ... ];
then to access :
handleptr* handle = (handlerptr*)[[handlers objectAtIndex:0] pointerValue];
handle(foo_bar);
In Objective-C, you don't pass around methods; you pass around selectors, which are basically the canonical names of methods. Then, to make an object respond to a selector message, you send it performSelector:. For example:
NSString *exampleString = [NSString stringWithString:#"Hello"];
SEL methodName = #selector(stringByAppendingString:);
// ^This is the selector. Note that it just represents the name of a
// message, and doesn't specify any class or implementation
NSString *combinedString = [exampleString performSelector:methodName withObject:#" world!"];
What you'll want is to make an array of NSStrings containing the names of the selectors you're interested in. You can use the function NSStringFromSelector() to do this. Then, when you want to use them, call NSSelectorFromString() on the strings to get the original selector back and pass it to the appropriate object's performSelector:. (As shown in the example above, the receiver isn't encoded in a selector ā€” just the method name ā€” so you might need to store the receiver as well.)