Objective-C enum-map - objective-c

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.

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

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

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.

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.)

What is better: Global string or singleton?

For my brand new ORM (chibi-ORM) I face a design decision.
I'm building the API for relationships, like:
#interface SamplePerson : DbObject {
NSString *name;
DbObjectRelation *cities;
}
Now, for DbObjectRelation I have this mockup:
#interface DbObjectRelation : NSObject {
NSMutableArray *childRows;
DbObject *parent;
BOOL isLoaded;
}
-(void) load;
-(void) addObject:(DbObject *) childRow;
-(void) removeObject:(DbObject *) childRow;
- (id)initWitParent:(DbObject *)parentObject;
So, I need a way inside load of know which database use for load the records.
I think in have in my DB connection something like:
static NSString *currentDbNameSingleton = nil;
+(NSString *)currentDbName {
#synchronize( self ) {
if ( currentDbNameSingleton == nil ) {
currentDbNameSingleton = [[NSString alloc]
}
}
return sharedInst;
}
+(void) setCurrentDbName(NSString *)name {
#synchronize( self ) {
currentDbNameSingleton = [name copy];
}
}
But wonder if is better build the DB class as a singleton. This is for iPhone projects...
Using a set of class accessors/mutators around a static variable like you're currently doing is pretty reasonable. Another pretty common solution if you're not going to change the database name would be to put it in a separate file, say Constants.h, along with other constant strings like dictionary and user defaults keys.
If you have additional code related to your database that you can refactor into a single place, then that's a pretty good point to built a singleton class for your database stuff. I wouldn't create a class just to hold a string though.
For strings that don't change you should create them as constants.
i.e. In your database connection file:
const NSString *CurrentDbName = #"name of database";
Note the capitlization to convey compile-time generation and constant-ness.
It's slightly more efficient than a singleton for a string. Of course, you can't do this with other types of Objective-C object because they can't be created by the compiler. For these, use a singleton.
If other parts of your program need access to the string, you should use an extern reference in your database's header file:
extern const NSString *CurrentDbName;

How can I pass a class name as an argument to an object factory in cocoa?

I am working on an object factory to keep track of a small collection of objects. The objects can be of different types, but they will all respond to createInstance and reset. The objects can not be derived from a common base class because some of them will have to derive from built-in cocoa classes like NSView and NSWindowController.
I would like to be able to create instances of any suitable object by simply passing the desired classname to my factory as follows:
myClass * variable = [factory makeObjectOfClass:myClass];
The makeObjectOfClass: method would look something like this:
- (id)makeObjectOfClass:(CLASSNAME)className
{
assert([className instancesRespondToSelector:#selector(reset)]);
id newInstance = [className createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
Is there a way to pass a class name to a method, as I have done with the (CLASSNAME)className argument to makeObjectOfClass: above?
For the sake of completeness, here is why I want to manage all of the objects. I want to be able to reset the complete set of objects in one shot, by calling [factory reset];.
- (void)reset
{
[managedObjects makeObjectsPerformSelector:#selector(reset)];
}
You can convert a string to a class using the function: NSClassFromString
Class classFromString = NSClassFromString(#"MyClass");
In your case though, you'd be better off using the Class objects directly.
MyClass * variable = [factory makeObjectOfClass:[MyClass class]];
- (id)makeObjectOfClass:(Class)aClass
{
assert([aClass instancesRespondToSelector:#selector(reset)]);
id newInstance = [aClass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
I have right a better tutorial on that , please checkout
https://appengineer.in/2014/03/13/send-class-name-as-a-argument-in-ios/
It's pretty easy to dynamically specify a class, in fact you can just reference it by it's name:
id string = [[NSClassFromString(#"NSString") alloc] initWithString:#"Hello!"];
NSLog( #"%#", string );
One other tip, I would avoid using the nomenclature 'managed object' since most other Cocoa programmers will read that as NSManagedObject, from Core Data. You may also find it easier to use a global NSNotification (that all your reset-able objects subscribe to) instead of managing a collection of different types of objects, but you're more informed to make that decision than I am.
The bit of the answer missing from the other answers is that you could define a #protocol containing your +createInstance and +reset methods.
It sounds like you want something like:
- (id)makeObjectOfClassNamed:(NSString *)className
{
Class klass = NSClassFromString(className);
assert([klass instancesRespondToSelector:#selector(reset)]);
id newInstance = [klass createInstance];
[managedObjects addObject:newInstance];
return newInstance;
}
This would assume a class method named +createInstance. Or you could just use [[klass alloc] init].
To call it:
MyClass *variable = [factory makeObjectOfClassNamed:#"MyClass"];
Depending on what you're trying to do, it might be better to pass around class objects than strings, e.g.:
MyClass *variable = [factory makeObjectOfClass:[MyClass class]];