How to fill NSArray in compile time? - objective-c

In Objective-C, how to do something like is
int array[] = {1, 2, 3, 4};
in pure C?
I need to fill NSArray with NSStrings with the smallest overhead (code and/or runtime) as possible.

It's not possible to create an array like you're doing at compile time. That's because it's not a "compile time constant." Instead, you can do something like:
static NSArray *tArray = nil;
-(void)viewDidLoad {
[super viewDidLoad];
tArray = [NSArray arrayWithObjects:#"A", #"B", #"C", nil];
}
If it's truly important that you have this precompiled, then I guess you could create a test project, create the array (or whatever object) you need, fill it, then serialize it using NSKeyedArchiver (which will save it to a file), and then include that file in your app. You will then need to use NSKeyedUnarchiver to unarchive the object for use. I'm not sure what the performance difference is between these two approaches. One advantage to this method is that you don't have a big block of code if you need to initialize an array that includes a lot of objects.

use this
NSArray *array = [NSArray arrayWithObjects:str1,str2, nil];

As far as i understand you need a one-dimentional array
You can use class methods of NSArray.. For instance
NSString *yourString;
NSArray *yourArray = [[NSArray alloc] initWithObjects:yourString, nil];
If you need more, please give some more detail about your issue

Simple as that: NSArray<NSString*> *stringsArray = #[#"Str1", #"Str2", #"Str3", ...]; Modern ObjectiveC allows generics and literal arrays.
If you want shorter code, then NSArray *stringsArray = #[#"Str1", #"Str2", #"Str3", ...];, as the generics are optional and help only when accessing the array elements, thus you can later in the code cast back to the templatized array.

Related

Objective-c Workaround for running intepretted code

I'm used to using eval() in languages like javascript e.t.c.
In cocos2d, to select the level, I am passing "1" or "2" to my Loadlevel.m file, the level Classes are named "LevelOne" and "LevelTwo" accordingly, I wanted to create a dictionary lookup that paried "1" => "LevelOne" e.t.c then run eval on that string to effectively call [MyLevel node];
Apparently we can't use eval in IOS code, so how would I go about doing this?
Try using the NSStringFromClass and NSClassFromString functions.
Specifically, represent the classes as strings in your dictionary, e.g.,
[myDictionary setObject:NSStringFromClass([LevelOne class]) forKey:#"1"]
Then, to use the right level from your dictionary, you could do:
NSString *levelAsString = [myDictionary objectForKey:#"1"];
id node = [NSClassFromString(levelAsString) node]
(I'm assuming +node is a class method)
Now, this is a pretty uncommon and odd design in Cocoa. Perhaps if you explain more about what you're doing, we could suggest alternate design choices that might be better.
I'm not one hundred percent clear on what you're asking, but you can store class objects directly in your dictionary, retrieve them, and send them messages:
NSDictionary * d = [NSDictionary dictionaryWithObjectsAndKeys:[LevelOne class], #"1", [LevelTwo class], #"2", nil];
Class selectedLevel = [d objectForKey:#"1"];
[selectedLevel node];

array of strings in a constant with #define - objective c

I need a array of strings in a constant. is a good idea to use #define?
For example:
#define rows [NSArray arrayWithObjects: #"NameRowA",#"NameRowB",#"NameRowC", nil]
#define KEY_ROWA [columnas objectAtIndex:0]
#define KEY_ROWB [columnas objectAtIndex:1]
#define KEY_ROWC [columnas objectAtIndex:2]
I need to access to the array of strings and the elements of that array.
I have read, (i don´t know if is true) with this way it is created a new NSArray when it is used, I suppose then the array is released, so I think this is good because you only use that part of memory when you need it.
Use a singleton, it's just a couple of lines of code.
All you need is a static singleton, and a static method for retrieving it (and settings it once).
Your .m file should contain this:
#implementation MyClass
static NSArray *mySingletonArray; // this will be your array
+ (NSArray *)mySingletonArray // this is the static method for accessing your array
{
if (nil == mySingletonArray) {
mySingletonArray = [NSArray arrayWithObjects:#"firstString", #"secondString", nil];
}
return mySingletonArray;
}
Acquire what you need from you array using the static access method [MyClass mySingletonArray], e.g.
NSLog("%#", [[MyClass mySingletonArray] objectAtIndex:0]);
I don't think you want to use #define for this.
In your example, there is no constant array of strings made with this code. Every single time rows is used in your code, a new NSArray is going to be allocated. Also, KEY_ROWA refers to columnas, but that isn't in the rows define. I assume you have something like this
NSArray *columnas = rows;
There is really no difference between that and
NSArray *columnas = [NSArray arrayWithObjects: #"NameRowA",#"NameRowB",#"NameRowC", nil];
But the second line is a lot more obvious. The same is true with KEY_ROWA -- the objectAtIndex call would be more obvious and the macro doesn't get you anything.
I'm not sure what you need exactly, but if you need a shared constant array of strings inside of one class, you could declare it as + instead of - at the beginning, and allocate it once (this is a class variable as opposed to an instance variable). More info here:
How do I declare class-level properties in Objective-C?

objective-c how to create an enumeration array with its string values as keys?

i have an enumeration say gender, now i want to associate it to string values to use in the view inside a picker view. It's cocoa-touch framework and objective-c as language.
So i don't know of a way to set the data source of the picker view as the enumeration, as could have been done in other frameworks. So i've been told i have to make array of enum values. and then i tried to add thos into an NSMutableDictionary with their respective string values.
So i ended up with
NSArray* genderKeys = [NSArray arrayWithObjects:#"Male",#"Female",nil] ;
NSArray* genderValues = [NSArray arrayWithObjects:[NSNumber numberWithInt:male],[NSNumber numberWithInt:female],nil];
for(int i =0;i<[genderKeys count];i++)
[_genderDictionary setValue:[genderValues objectAtIndex:i] forKey:[genderKeys objectAtIndex:i]];
and it's not working saying it's not a valid key, and i've read the key-coding article and i know now what's key and whats keypath, but still how can i solve that. It's ruining my life, Please help.
Sorry guys, i was using NSDictionary for _genderDictionary.But i had in my mind that it was nsmutable. Thank you all.
Be careful using UI text as keys into your database. What amount when you need to localise your application to french, chinese, arabic etc?
That works for me. Running this (your code, with the first line added so it would compile) seems to work fine.
NSMutableDictionary *_genderDictionary = [NSMutableDictionary dictionary];
NSArray* genderKeys = [NSArray arrayWithObjects:#"Male",#"Female",nil] ;
NSArray* genderValues = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2],nil];
for(int i =0;i<[genderKeys count];i++)
[_genderDictionary setValue:[genderValues objectAtIndex:i] forKey:[genderKeys objectAtIndex:i]];
NSLog()-ing _genderDictionary outputs this
{
Female = 2;
Male = 1;
}
edit: re-reading your question, makes me think what you are looking for is the delegate methods of UIPickerView... implementing –pickerView:titleForRow:forComponent: is where you set the text that appears in the picker. If you have an NSArray of genders, you would do something like return [_genderArray objectAtIndex:row]; That way you don't need to fuss around with a dictionary and keys.
edit 2: a picker's datasource can't be an NSArray or NSDictionary directly. It has to be an object that implements UIPickerView's datasource/delegate protocol (which I suppose you could do with a subclass of NSArray, but that'd be cah-ray-zay!).
If I understand you correctly, you try to create a pre-populated dictionary.
You could use [NSDictionary dictionaryWithObjectsAndKeys:] for that.
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt:0], #"Male",
[NSNumberWithUnsignedInt:1], #"Female", nil]

Creating a constant dictionary object

I would like to accomplish something like what is being done in this post: Constants in Objective-C
however, i would like to construct an NSDictionary.
if i do something like:
constants.h
extern NSArray *const mFooKeys;
extern NSArray *const mFooObjects;
extern NSDictionary *const mFooDictionary;
constants.m
NSArray *const mFooKeys = [[NSArray alloc] initWithObjects:
#"Foo", #"Bar", #"Baz", nil];
NSArray *const mFooObjects = [[NSArray alloc] initWithObjects:
#"1", #"2", #"3", nil];
NSDictionary *const mFooDictionary = [[NSDictionary alloc] dictionaryWithObjects:mFooObjects
forKeys:mFooKeys];
do i release in dealloc and everything is fine, or is there more to it? this is more a cautious question than a 'something is wrong' question, but i feel like i could really mess this up without realizing it.
In order to have a constant like a NSDictionary that is based on other core data types, you either need to include it in the class that will be using the constant, or create a Singleton class and store the NSDictionary there. There just some class types that will not work in the implementation you are looking at; the constants code you are looking would need to be used as an object in order to work correctly, but I think that kind of defeats the purpose. I'm not clear as what the determining factor is for what you can and can't do in the simple constants implementation, but I ran into the same issue and the Singleton design pattern worked perfectly for me. (Either way, you should dealloc appropriately even though they will exist for the life of the application.)
You're declaring these as constants, so they are single objects that will last for the lifetime of your application. No need to release, as they're needed until the application is quit.
You don't want to release in dealloc, as this will release every time an instance of the relevant class is deallocated.

Bidirectional Map in Cocoa

Cocoa provides NSDictionary, which essentially is an associative array.
Is there a nice way to get bidirectional associativity? i.e. one way would have been if NSDictionary had a keyForObject: method which mirrored the behavior of objectForKey:.
I don't really care if NSDictionary is not the way to get this. I know NSDictionary does provide keysOfEntriesPassingTest: but it returns an NSSet * which doesn't look very clean for the kind of thing I want to have.
Here is an example:
fieldMap = [[NSDictionary alloc] initWithObjectsAndKeys:
#"c0",#"synckey",
#"c1",#"wi_id",
#"c2",#"wi_parent_id",
#"c3",#"wi_type",
#"c4",#"wi_rh_task",
#"c5",#"decision_key",
#"c6",#"wi_stat",
#"c7",#"wi_prio",
#"c8",#"wi_cd",
#"c9",#"wi_ct",
#"cb",#"wi_dh_stat",
#"cc",#"wi_user",
nil];
I want to translate between c0 and synckey and back, and ditto for the other fields.
The closest thing for what you're after is, I believe allKeysForObject:. This returns an NSArray containing the keys corresponding to all occurrences of a given object in the dictionary. Obviously if the object is in the dictionary only once, the NSArray will contain only one object.
So with your example:
fieldMap = [[NSDictionary alloc] initWithObjectsAndKeys:
#"c0",#"synckey",
#"c1",#"wi_id",
#"c2",#"wi_parent_id",
#"c3",#"wi_type",
#"c4",#"wi_rh_task",
#"c5",#"decision_key",
#"c6",#"wi_stat",
#"c7",#"wi_prio",
#"c8",#"wi_cd",
#"c9",#"wi_ct",
#"cb",#"wi_dh_stat",
#"cc",#"wi_user",
nil];
This additional code would return an array containing 1 string object evaluating to #"c7":
NSArray *keyArray = [fieldMap allKeysForObject:#"wi_prio"];
[Aside: Note that this would only work here because of how the compiler works; it takes all occurences of #"wi_prio" and makes them the same object. If instead you had perhaps loaded the dictionary from disk etc, this approach will not work for NSStrings. Instead you should probably use allKeys and then iterate through them, comparing with [mystring isEqualToString:anotherString].]
The CHDataStructures framework has CHBidirectionalDictionary.