What is better: Global string or singleton? - objective-c

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;

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

Managing list of error codes for NSError in objective c

In Cocoa and Objective C the favorite method for managing error seems to be using an NSError * object, to construct an error object however, we need to call the following method
+ (id)errorWithDomain:(NSString *)domain code:(NSInteger)code userInfo:(NSDictionary *)dict
My question is, what are some of the best practices for managing error domain, error code definitions and user info dictionary across the entire application so that error code, domain and user info dict always stays consistent?
If you have a hefty amount of error construction, your life could be much simpler by using a class. I actually use C++ for this so the calls a program does not need may be removed (unlike objc), but you can use C, ObjC, or C++ for this:
MONErrorDomain.h
// you won't normally need an instance here
#interface MONErrorDomain : NSObject
+ (NSString *)domain; // << required override
- (NSString *)domain; // << returns [[self class] domain]
// example convenience methods:
// uses [self domain]
+ (NSError *)errorWithErrorCode:(NSInteger)errorCode; // << user info would be nil
+ (NSError *)errorWithErrorCode:(NSInteger)errorCode userInfo:(NSDictionary *)userInfo;
#end
MONKoalaError.h
#interface MONKoalaError : MONErrorDomain
+ (NSError *)outOfEucalyptus;
#end
extern NSString * const MONKoalaErrorDomain;
typedef enum MONKoalaErrorCode {
MONKoalaErrorCode_Undefined = 0,
MONKoalaErrorCode_OutOfEucalyptus
} MONKoalaErrorCode;
MONKoalaError.m
// apple recommends we use reverse domains
NSString * const MONKoalaErrorDomain = #"com.mon.koala-library.MONKoalaErrorDomain";
#implementation MONKoalaError
+ (NSString *)domain
{
return MONKoalaErrorDomain;
}
+ (NSError *)outOfEucalyptus
{
NSDictionary * info = …;
return [self errorWithErrorCode:MONKoalaErrorCode_OutOfEucalyptus userInfo:info];
}
#end
Then the error creation is all in one place for each domain, and the clients can easily pick their errors without actually building them manually:
if (outError) {
*outError = [MONKoalaError outOfEucalyptus];
}
and error handling takes the form:
if ([e.domain isEqualToString:MONKoalaErrorDomain]) {
switch (e.code) {
case MONKoalaErrorCode_OutOfEucalyptus : {
self.needsEucalyptus = true;
…
One common way is to define some appropriate constants in a header file, and then include that header file wherever needed. It's a pretty simple approach, and looks like:
const NSString * kMyAppErrorDomain = #"com.example.myapp";
const NSInteger kMyAppSomeError = 2;
// key into user info dictionary
const NSString * kMyAppProblemKey = #"MyAppProblemKey";
I've also seen some applications which create convenience methods for creating these, either as a category on NSError or as a separate utility class or set of functions. It's also entirely reasonable to subclass NSError, for example to customize the localized description.
If you have not already seen it, Apple has released the Error Handling Programming Guide which discusses how these should be used in Cocoa.

Access instance variable via static method

When I run this :
#interface Database : NSObject {
sqlite3 *database;
}
+(void)openDatabase;
#end
#implementation Database
+(void)openDatabase
{
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *databaseNameandPath = [NSString stringWithFormat:#"%#/DatabaseSnax.sqlite",docDir];
NSString *databasePath=[[NSString alloc]initWithFormat:databaseNameandPath];
if(sqlite3_open([databasePath UTF8String], &database) != SQLITE_OK)
{
NSLog(#"Error when open the database");
}
[databasePath release];
}
I have this error: instance variable database accessed in class method
How I can solve this issue and I need to keep my method (open database) as a static method so I can use it by class name, for example:
[Database openDatabase];
One cannot possibly access instance variables from class methods. You may declare a global variable, however:
static sqlite3 *database;
// ...
+ (void) openDatabase {
sqlite3_open(filename, &database);
// ...
}
You're attempting to access database from a class method (which are different from instance methods).
Change that declaration from:
+ (void) openDatabase;
to
- (void) openDatabase;
and instantiate your Database object via the traditional alloc + init and you'll be on your way.
I like H2CO3's answer too (and +1 to him), but my answer (which is what most people have to do with Objective C objects) may be more practical for what you are trying to do.
For reference, static means different things to different people/languages. Objective-C being mostly C plus a bunch of syntax enhancements, the keyword static in Objective-C has the same meaning as it does in C, which relates to the visibility of the symbol with respect to linking. This is subtly-but-importantly different from how Java and C# use the word static.
Objective-C doesn't have syntax for declaring "static" (in Java/C# parlance) or "class" variables. The runtime has "support" for them (witness the existence of: class_getClassVariable) but there's no syntax to declare them, so it's sort of a dead end. (If I had to guess, I'd bet that this feature exists in the runtime to support bridges to other languages/runtimes that use static/class variables.) As other people have suggested, the common way around this is to use global variables (or function statics (static in the C linkage sense.))

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.

How to detect a property return type in Objective-C

I have an object in objective-c at runtime, from which I only know the KVC key and I need to detect the return value type (e.g. I need to know if its an NSArray or NSMutableArray) of this property, how can I do that?
You're talking about runtime property introspection, which happens to be something that Objective-C is very good at.
In the case you describe, I'm assuming you have a class like this:
#interface MyClass
{
NSArray * stuff;
}
#property (retain) NSArray * stuff;
#end
Which gets encoded in XML something like this:
<class>
<name>MyClass</name>
<key>stuff</key>
</class>
From this information, you want to recreate the class and also give it an appropriate value for stuff.
Here's how it might look:
#import <objc/runtime.h>
// ...
Class objectClass; // read from XML (equal to MyClass)
NSString * accessorKey; // read from XML (equals #"stuff")
objc_property_t theProperty =
class_getProperty(objectClass, accessorKey.UTF8String);
const char * propertyAttrs = property_getAttributes(theProperty);
// at this point, propertyAttrs is equal to: T#"NSArray",&,Vstuff
// thanks to Jason Coco for providing the correct string
// ... code to assign the property based on this information
Apple's documentation (linked above) has all of the dirty details about what you can expect to see in propertyAttrs.
Cheap answer: use the NSObject+Properties source here.
It implements the same methodology described above.
The preferred way is to use the methods defined in the NSObject Protocol.
Specifically, to determine if something is either an instance of a class or of a subclass of that class, you use -isKindOfClass:. To determine if something is an instance of a particular class, and only that class (ie: not a subclass), use -isMemberOfClass:
So, for your case, you'd want to do something like this:
// Using -isKindOfClass since NSMutableArray subclasses should probably
// be handled by the NSMutableArray code, not the NSArray code
if ([anObject isKindOfClass:NSMutableArray.class]) {
// Stuff for NSMutableArray here
} else if ([anObject isKindOfClass:NSArray.class]) {
// Stuff for NSArray here
// If you know for certain that anObject can only be
// an NSArray or NSMutableArray, you could of course
// just make this an else statement.
}
This is really a comment addressing an issue raised by Greg Maletic in response to answer provided by e.James 21APR09.
Agreed that Objective-C could use a better implementation for getting these attributes.
Below is a method I quickly threw together to retrieve attributes of a single object property:
- (NSArray*) attributesOfProp:(NSString*)propName ofObj:(id)obj{
objc_property_t prop = class_getProperty(obj.class, propName.UTF8String);
if (!prop) {
// doesn't exist for object
return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:#","];
return attrArray;
}
Partial list of attribute keys:
R Read-only
C Copy of last value assigned
& Reference to last value assigned
N Nonatomic property
W Weak reference
Full list at Apple
You can use isKindOfClass message
if([something isKindOfClass:NSArray.class])
[somethingElse action];
If you know that the property is defined :
id vfk = [object valueForKey:propertyName];
Class vfkClass = vfk.class;
And compare with isKindOfClass, isSubClass, etc.