Objective-c Workaround for running intepretted code - objective-c

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];

Related

NSArray create with some elements plus another NSArray

I am trying to use model inheritance on realm. So I minded up using the code below to override and also call super method.
+ (NSArray *)requiredProperties {
return #[[super requiredProperties], #"thisIsRequired",#"thisIsAlsoRequired"];
}
So the question: is it OK to create an NSArray on the fly while also using another NSArray and some more elements:
NSArray *mySecondArray = #[myFirstArray, #"andSomeExtras", #"alsoMoreExtras"];
What I have been expecting is; first element of mySecondArray should be the first element of myFirstArray. Second element of mySecondArray should be the second element of myFirstArray and so on. (size of myFirstArray) +1 th element of mySecondArray should be #"thisIsRequired" .
Or I am making up some kind of magix?
Well, as you can see I am new to the stuff and I might be confused.
In general, it is okay to instantiate such heterogeneous arrays with Foundation. It's just not what you want here. In your example, you would end up with the following instead:
NSArray *myFirstArray = #[#"firstsFirstsElement", #"firstsSecondElement"];
NSArray *mySecondArray = #[myFirstArray, #"andSomeExtras", #"alsoMoreExtras"];
/* =>
#[
#[#"firstsFirstsElement", #"firstsSecondElement"],
#"andSomeExtras",
#"alsoMoreExtras",
]
*/
You're looking for - arrayByAddingObjectsFromArray:. You can use it like seen below:
+ (NSArray *)requiredProperties {
return [super.requiredProperties arrayByAddingObjectsFromArray:#[
#"thisIsRequired",
#"thisIsAlsoRequired",
]];
}
Yes it is Ok, as long as you will remember what you are doing anywhere in your code.
However you usually reserve the use of NSArray for a homogeneous collection of objects and use NSDictionary, or better your own DTO class, for heterogeneous aggregation of data.
NSArray official documentation show a similar example in the section dedicated to the method arrayWithObjects:
NSArray is really an array of id, even with generics enabled.
This other link about Objective C generics may also help understand the nature of the raw NSArray class.
Why not use NSMutableArray and just add the objects?
NSMutableArray *mySecondArray = [myFirstArray mutableCopy];
[mySecondArray addObject:#"andSomeExtras"];
[mySecondArray addObject:#"alsoMoreExtras"];

How to fill NSArray in compile time?

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.

Objective-C: Using an NSString variable to send a message to one of a variety of objects

I'll use an example from JavaScript to help clarify my question. Let's assume I have the following object:
sports = {
soccer: {...},
basketball: {...},
baseball: {...}
}
If at some point in my script I have a variable, sportString, that simply holds a string, I can dynamically call one of the sports objects in the following way:
sports[sportString];
This frees me from having to use a bunch of nested if statements, testing the value of the string such as:
if(sportString === 'soccer'){
sports.soccer;
}else if(sportString === 'basketball){....
So, my question is how can I accomplish something similar to sports[sportString] in Objective-C, if sportString is an NSString object?
Use an NSDictionary as your sports object. Then you can do lookups like this:
[sports objectForKey: sportsString];
The people saying you should use NSDictionary for general key/value storage are 100 % right. However, I think it’s useful to know that you can call a message specified by a string:
SEL selector = NSSelectorFromString(#"foo"); // Or #selector(foo) if you know it at compile time
id value = [object performSelector:selector];
You can also use selectors with up to two arguments, as long as they take objects:
SEL selector2 = NSSelectorFromString(#"setFoo:");
[object performSelector:selector2 withObject:value];
It’s possible to invoke arbitrary methods using IMPs or casting objc_msgSend(), but now I’m getting way beyond the scope of your actual question. :-)
Your JavaScript object sports would typically be an NSDictionary or NSMutableDictionary.
Example:
NSMutableDictionary *sports = [NSMutableDictionary dictionary];
[sports setObject:#"Foo" forKey:#"soccer"];
[sports setObject:#"Bar" forKey:#"basketball"];
NSString *sportString = #"soccer";
NSString *sportValue = [sports objectForKey:sportString];
NSLog(#"%#", sportValue); //logs "Foo"

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.

Objective-C arrays, style and spirit

Newbie question. Looking at arrays (ie: dynamically sized) this works:
NSArray *array;
array = [NSArray arrayWithObjects:
#"one", #"two", nil];
This does not:
array = [NSArray arrayWithObjects:
1, 2, nil];
Ok, I get it. This works:
array = [NSArray arrayWithObjects:
[NSNumber numberWithInt:1], [NSNumber numberWithInt:2], nil];
Its sorta less "on the fly" as C++ / Java. I see the same thing with the init examples I'm reading. For example:
// pseudo objc example
MyVar v = [MyVar init]; // blank
[v setSomething];
[v setSomethingElse];
// use v down here
In C++/Java I'd do:
MyVar v = new MyVar("foo", "bar", "baz", "quux");
And I'd know that v is ready to go by default. Is there a spirit of ObjC that I should not fight? Should I just expect to write more lines and less "one-liners"?
In Objective-C, the "init" method is just a method. Unlike Java or C++ whose constructors are different than other methods. So you can define your own init methods that behave like C++ or Java constructors. For example, you could define an init method that takes several parameters. It might look something like this.
MyVar* v = [[MyVar alloc] initWithName:#"foo" andTitle:#"bar"];
// do something with v
[v release];
Common practice is to simply create new methods that perform object initialization, and prefix the method name with "init" for clarity and consistency.
NSArray, its modifiable variant NSMutableArray, and all collection structures (NSDictionary, NSSet) are made for storing objects. This is why you see [NSNumber numberWithInt:1] instead of simply 1.
For strings, note that an Objective-c string (like #"one", including leading #) is an object of type NSString, whereas a C string (like "one", without #) is not an object.
If you want the simplicity of storing simple values in arrays, don't forget that Objective-C is a superset of C. This means that you can use a declaration like:
int array[] = { 1, 2, 3 };
What you get with Objective-C's verbosity is greater readability.
Sure, while you're writing code, it might be easy to knock up something like you wrote
MyRect rect = new MyRect(1.0, 1.0, 3.0 3.0);
But when you, or more likely someone else, comes to maintain your code then I would argue that this is much easier to read:
MyRect *rect = [[MyRect alloc] initWithX:1.0 Y:1.0 width:3.0 height:3.0];
And in these days of smart editors it isn't that much harder to write.
You can write constructors that take named parameters to get it down to one line:
MyVar *v = [[MyVar alloc] initWithFoo:#"foo" bar:#"bar" baz:#"baz" quux:#"quux"];
Yeah, Objective-C is pretty verbose, so you're going to need to get used to that.
The MyVar init case is not quite right; you generally need to do a [[MyVar alloc] init] or a [MyVar new]. And in many cases, there are init variants that take arguments, like
MyVar v = [[MyVar alloc] initWithSomething: "foo" somethingElse: "bar"];
If you want something less verbose, where you can get more one liners in, you might want to look into MacRuby, which is a binding between Ruby and Objective-C that gives you access to all of the Cocoa APIs but with a much more compact, high level syntax (and you can go even more compact still with HotCocoa. Of course, there is a bit of a performance penalty using MacRuby, so if you're doing a high performance game it might not be ideal to write your drawing loop in RubyCocoa, but its fine for the vast majority of applications.