Managing list of error codes for NSError in objective c - 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.

Related

Macro that logs the actual types of method arguments

Let's say this is my init method
- (id)initWithClient:(id <Client>)client
andDataStorage:(DataStorage *)storage
{
if (self = [super init])
{
self.client = client;
self.storage = storage;
}
return self;
}
Then I want to write a macro that somehow logs the parameters passed to a method, by wrapping the parameter with a defined macro. Is this possible in any way?
The problem is at runtime it's not possible to find out the type of a parameter passed to a method. So I'm trying to find a hack around it, and do it at compile time.
// somehow achieve this, and log the value inside the marco
#define INJECT(x) NSLog(#"%#", x)
- (id)initWithClient:(INJECT(id <Client>))client
andDataStorage:(INJECT(DataStorage *))storage
{
}
expected log in console:
id <Client>
DataStorage *
At the risk of running into what appear to be crossed wires in the comments: you can get the parameter types passed to a method at runtime.
E.g.
NSMethodSignature *signature =
[class methodSignatureForSelector:#selector(someSelector:)];
for(int argument = 2; argument < signature.numberOfArguments; argument++)
{
const char *argumentType = [signature getArgumentTypeAtIndex:argument];
// this is where it gets a bit messy...
if(!strcmp(argumentType, #encode(int))) NSLog(#"an integer");
if(!strcmp(argumentType, #encode(float))) NSLog(#"a float");
// ... etc, etc, etc ...
}
For any passed objects, use [object class] since all objects look the same at the runtime level — think of e.g. NSArray -addObject:; the runtime knows an object type will be passed in but it could be any object type.
See Apple's documentation on Type Encodings for information on what's going on there with those #encodes.
Whilst not an answer to the question as such. I would not recommend doing what you are asking about. I've seen far to much code where people have logged every single method call and argument (horribly over-complicated Java Enterprise stuff). The result has always been obscenely large logs that tell you next to nothing because of the amount of work it takes to find what you are after.
My recommendation would be that logging is important, but you should do targeted logging that clearing shows the state of relevant data at specific points which are important to understanding the flow.
Like others, I'm not sure what you are really after, or whether it is a good idea/design etc. But I wonder whether you are approaching the problem the wrong way. So let's take a look and maybe it will help you. From what I see you:
Want to find some way of obtaining the declared types of method parameters, in the form of strings, at runtime.
You are trying to tackle this by adding macros to the source. This tells me that you are not trying to do this for methods in a binary library that you are dynamically loading, but to methods in source you are compiling and are prepared to modify to achieve your goal.
Looked at that way, what is the problem? If you are prepared to add macros to your source why not simply add data declarations that contain the information you want - a mapping from a selector to an order list of parameter types as strings.
Is the issue that you want to extract the information in some automated way and were intending adding your macros by some automated process?
You can arrange for an Xcode project to run a source file through some other program by changing the file extension. Apple provide examples of using this to pre-process strings files - the files are fed through a Ruby script which produces a strings file which Xcode then handles as usual. Will that address your needs? Could you write a script/application (doesn't need to be in Ruby) which could add the information you need "on the fly" - take source in, produce modified source out which Xcode then compiles as usual? Note that the Clang compiler itself is designed to be called as a library so you can even use it to help you parse your source to extract the information you are after.
If none of those approaches suit consider that the debugger knows the correct types at runtime, and it gets those from the symbol information generated for it. There are library functions provided to help reader debugger information, so you should be able to write code which uses the same information the debugger does.
Hope those ideas help you, though I'm still not clear what you are trying or whether it makes sense!
due to objC being dynamically typed, all classes have the type id. The information about the declared types is erased. They are merely hints for the developer and to enable the compiler to do some type checking (again purely for the dev's benefit)
So while #encode works for 'primates' and structs and stuff, for classes all is equal... as there are not really object types for runtime
'Solution': Store the class names of method argumentsin a map manually and then COMBINE that info with #encode;s info to log the stuff.
working sample:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
NSDictionary *DDParamsMap(void);
NSDictionary *DDParamsMap() {
static NSDictionary *dict = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//TODO
//add all methods that are have objc classes passed
//add the classes or NSNull
dict = #{#"Test_initWithArray:data:number:": #[NSArray.class, NSData.class, NSNull.null]};
});
return dict;
}
void DDLogParamsOf(Class class, SEL sel);
void DDLogParamsOf(Class class, SEL sel) {
//
//try internal lookup first (so we get class names
//
NSString *className = #(class_getName(class));
NSString *methodName = NSStringFromSelector(sel);
NSString *key = [NSString stringWithFormat:#"%#_%#", className, methodName];
NSArray *types = DDParamsMap()[key];
//
// loop
//
NSMethodSignature *signature = [class instanceMethodSignatureForSelector:sel];
if(!signature) {
signature = [class methodSignatureForSelector:sel];
}
//if the array doesnt have the right number of values, screw it!
if(types.count != signature.numberOfArguments - 2) {
types = nil;
}
for(int argument = 2; argument < signature.numberOfArguments; argument++) {
id type = types[argument - 2];
if(type && ![type isKindOfClass:[NSNull class]]) {
NSLog(#"class is %#", type);
}
else {
const char *argumentType = [signature getArgumentTypeAtIndex:argument];
// this is where it gets a bit messy...
if(!strcmp(argumentType, #encode(int))) NSLog(#"an integer");
if(!strcmp(argumentType, #encode(float))) NSLog(#"a float");
if(!strcmp(argumentType, #encode(id))) NSLog(#"it is a class");
// ... etc, etc, etc ...
}
}
}
#define LogParams() DDLogParamsOf(self.class, _cmd);
#interface Test : NSObject
+ (void)testMethofWithFloat:(float)f;
- (id)initWithArray:(NSArray*)a
data:(NSData*)d
number:(int)i;
#end
#implementation Test
+ (void)testMethofWithFloat:(float)f {
LogParams();
}
- (id)initWithArray:(NSArray*)a
data:(NSData*)d
number:(int)i
{
LogParams();
return nil;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
[Test testMethofWithFloat:3.0f];
Test *t = [[Test alloc] initWithArray:#[] data:[NSMutableData data] number:1];
t = nil;
}
}

Why does "conformsToProtocol" not check for "required" method implementations?

I am trying to enforce a "formal" #protocol, but cannot reliably test my classes/instances as to whether they ACTUALLY implement the protocol's "required" methods, vs. simply "declaring" that they conform to the protocol.
A complete example of my quandary…
#import <Foundation/Foundation.h>
#protocol RequiredProtocol
#required
- (NSString*) mustImplement; #end
#interface Cog : NSObject <RequiredProtocol> #end
#implementation Cog #end
#interface Sprocket : NSObject #end
#implementation Sprocket
- (NSString*) mustImplement
{ return #"I conform, but ObjC doesn't care!"; } #end
int main(int argc, char *argv[]) {
Protocol *required = #protocol(RequiredProtocol);
SEL requiredSEL = #selector(mustImplement);
void (^testProtocolConformance)(NSObject*) = ^(NSObject *x){
NSLog(#"Protocol:%#\n"
"Does %# class conform:%# \n"
"Do instances conform:%# \n"
"Required method's result:\"%#\"",
NSStringFromProtocol ( required ),
NSStringFromClass ( x.class ),
[x.class conformsToProtocol:required] ? #"YES" : #"NO",
[x conformsToProtocol:required] ? #"YES" : #"NO",
[x respondsToSelector:requiredSEL] ? [x mustImplement]
: nil );
};
testProtocolConformance ( Cog.new );
testProtocolConformance ( Sprocket.new );
}
Result:
Protocol:RequiredProtocol
Does Cog class conform:YES
Do instances conform:YES
Required method's result:"(null)"
Protocol:RequiredProtocol
Does Sprocket class conform:NO
Do instances conform:NO
Required method's result:"I conform, but ObjC doesn't care!"
Why is it that a class and it's instances that DO implement the #protocol's methods (Sprocket) return NO to conformsToProtocol?
And why does one that DOESN'T ACTUALLY conform, but SAYS that it DOES (Cog) return YES?
What is the point of a formal protocol if the declaration is all that's needed to feign conformance?
How can you ACTUALLY check for complete implementation of multiple #selectors without MULTIPLE calls to respondsToSelector?
#Josh Caswell.. Without diffing the two.. I'd guess that your response achieves similar effect to the category on NSObject I've been using in the meantime…
#implementation NSObject (ProtocolConformance)
- (BOOL) implementsProtocol:(id)nameOrProtocol {
Protocol *p = [nameOrProtocol isKindOfClass:NSString.class]
? NSProtocolFromString(nameOrProtocol)
: nameOrProtocol; // Arg is string OR protocol
Class klass = self.class;
unsigned int outCount = 0;
struct objc_method_description *methods = NULL;
methods = protocol_copyMethodDescriptionList( p, YES, YES, &outCount);
for (unsigned int i = 0; i < outCount; ++i) {
SEL selector = methods[i].name;
if (![klass instancesRespondToSelector: selector]) {
if (methods) free(methods); methods = NULL; return NO;
}
}
if (methods) free(methods); methods = NULL; return YES;
}
#end
Conforming to a protocol is just a "promise", you can't know if the receiver of conformsToProtocol: actually implements all the required methods. Is enough that you declare that the class conforms to the protocol using the angle brackets syntax, and conformsToProtocol: will return yes:
Discussion
A class is said to “conform to” a protocol if it adopts the protocol or inherits from another class that adopts it. Protocols are adopted by listing them within angle brackets after the interface declaration.
Full source: NSObject's conformsToProtocol: .
Protocols declarations have just the advantage that you can know at compile time if a class really adopts that required methods. If not, a warning will be given. I suggest to don't rely on conformsToProtocol:, but to use introspection instead. That is, verify if a class/object implements a method by calling instancesRespondToSelector: / respondsToSelector: :
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
- (BOOL)respondsToSelector:(SEL)aSelector;
What compiler are you using? Xcode/Clang issues 2 warnings and 1 error...
Think of a protocol as a club with membership requirements. Asking whether someone is a member of the club, provable by them having a membership card (NSObject<ReqiredProtocol>), should tell you that a person meets those requirements. However the lack of a membership doesn't mean they don't meet the requirements.
E.g. someone (Sprocket) might meet all the requirements to join but choose not to. Someone else (Cog) may failed to meet the requirements but a sloppy administrator might let them in.
The latter is why I asked about the compiler (the sloppy administrator ;-)). Try your code as entered on Xcode 4.6.3/Clang 4.2 produces warnings and errors (as does using GCC 4.2):
The warnings state that Cog fails to implement the required methods;
The error complains about [x mustImplement] as x is not known to have the required method as it is of type NSObject - you need to cast to remove that, just [(id)x mustImplement] will do as you've already tested the method exists.
In summary, you can only rely on conformsToProtocol if you know the originator of the code didn't ignore compiler warnings - the checking is done at compile time.
Addendum
I missed the last sentence of your question. If you wish to discover whether a class meets the requirements of a protocol, even if it doesn't declare that it does, e.g. Sprocket above (or if you are obtaining code from folk who ignore compiler warnings - the Cog author above), then you can do so using the facilities of the Obj-C runtime. And you'll only have to write one call to repsondsToSelector...
I just typed in the following and quickly tested it on your sample. It is not throughly tested by any means, caveat emptor etc. Code assumes ARC.
#import <objc/runtime.h>
#interface ProtocolChecker : NSObject
+ (BOOL) doesClass:(Class)aClass meetTheRequirementsOf:(Protocol *)aProtocol;
#end
#implementation ProtocolChecker
+ (BOOL) doesClass:(Class)aClass meetTheRequirementsOf:(Protocol *)aProtocol
{
struct objc_method_description *methods;
unsigned int count;
// required instance methods
methods = protocol_copyMethodDescriptionList(aProtocol, YES, YES, &count);
for (unsigned int ix = 0; ix < count; ix++)
{
if (![aClass instancesRespondToSelector:methods[ix].name])
{
free(methods);
return NO;
}
}
free(methods);
// required class methods
methods = protocol_copyMethodDescriptionList(aProtocol, YES, NO, &count);
for (unsigned int ix = 0; ix < count; ix++)
{
if (![aClass respondsToSelector:methods[ix].name])
{
free(methods);
return NO;
}
}
free(methods);
// other protocols
Protocol * __unsafe_unretained *protocols = protocol_copyProtocolList(aProtocol, &count);
for (unsigned int ix = 0; ix < count; ix++)
{
if (![self doesClass:aClass meetTheRequirementsOf:protocols[ix]])
{
free(protocols);
return NO;
}
}
free(protocols);
return YES;
}
#end
You should of course want to know exactly how this works, especially the * __unsafe_unretained * bit. That is left as an exercise :-)
CRD is right; the compiler tells you about actual conformance, and it should be listened to. If that's being ignored, the runtime doesn't have any built-in method to double-check. Classes maintain internal lists of protocol objects internally; conformsToProtocol: just looks at that.
At the risk that someone is going to come along and tell me to stop fiddling with the ##(%!^& runtime again, if you really truly need to check actual implementation, this is one way you can do so:
#import <objc/runtime.h>
BOOL classReallyTrulyDoesImplementAllTheRequiredMethodsOfThisProtocol(Class cls, Protocol * prtcl)
{
unsigned int meth_count;
struct objc_method_description * meth_list;
meth_list = protocol_copyMethodDescriptionList(p,
YES /*isRequired*/,
YES /*isInstanceMethod*/,
&meth_count);
/* Check instance methods */
for(int i = 0; i < meth_count; i++ ){
SEL methName = meth_list[i].name;
if( ![class instancesRespondToSelector:methName] ){
/* Missing _any_ required methods means failure */
free(meth_list);
return NO;
}
}
free(meth_list);
meth_list = protocol_copyMethodDescriptionList(p,
YES /*isRequired*/,
NO /*isInstanceMethod*/,
&meth_count);
/* Check class methods, if any */
for(int i = 0; i < meth_count; i++ ){
SEL methName = meth_list[i].name;
if( ![class respondsToSelector:methName] ){
free(meth_list);
return NO;
}
}
free(meth_list);
return YES;
}
If I had a hammer...
All of these answers are good. To them, I would add one more point: calling conformsToProtocol: is almost always a mistake. Because it tells whether the class says that it conforms to the protocol, rather than whether it actually provides specific methods:
It is possible to create a class that claims to conform, but does not, by silencing various warnings, resulting in crashes if you assume that a required method exists.
It is possible to create a class that conforms to the protocol but does not claim to do so, resulting in methods not getting called on a delegate even though they exist.
It can lead to programming errors creeping in when the protocol changes, because your code checks for conformance to a protocol before calling a method that used to be required, but no longer is.
All of these issues can cause unexpected behavior.
IMO, if you want to know if a class handles a method, the safest approach is to explicitly ask it if it handles that method (respondsToSelector:), rather than asking it if it conforms to a protocol that just happens to contain that method.
IMO, conformsToProtocol: should really have been a function in the Objective-C runtime instead of being exposed on NSObject, because it generally causes more problems than it solves.

How to set expectations on parameters to mocked methods in Kiwi

Using OCMockito and OCHamcrest, I can set up expectations on the arguments to mocked methods, thusly:
[verify(aMockObject) doSomething:allOf(is(instanceOf([NSArray class])), hasCountOf(3U), nil)];
There doesn't seem to be an equivalently simple way to do this using Kiwi. It is possible to capture arguments using a spy, something like:
KWCaptureSpy *spy = [aMockObject captureArgument:#selector(doSomething:) atIndex:0];
NSArray *capturedArray = spy.argument;
And then to check expectations on the captured object:
[[capturedArray should] haveCountOf:3U];
Is there a less clumsy way to do this in Kiwi?
(I'm aware I could probably use hamcrest matchers in here, but for the moment I'm exploring what Kiwi is capable of).
One option that I have used is stub:withBlock:
NSArray* capturedArray; // declare this as __block if needed
[aMockObject stub:#selector(doSomething:)
withBlock:^id(NSArray *params) {
capturedArray = params[0];
// this is necessary even if the doSomething method returns void
return nil;
}];
// exercise your object under test, then:
[[capturedArray should] haveCountOf:3U];
This works fine, and I find it easier to implement than the spy pattern. But your question made me wonder about expectations using message patterns. For example:
[[[aMockObject should] receive] doSomething:myArray];
[[[aMockObject should] receive] doSomething:any()];
The first example will verify that aMockObject received the doSomething: message with an argument that isEqual:myArray. The second example will simply verify that doSomething: was sent, with no expectation about the array arugment. It would be great if we can specify some type of Matcher in the message pattern, to express that we don't care what specific array instance is sent in the message, just that it has a count of 3.
I haven't found any examples of being able to do this, but it looks like there are some possibilities. To verify a message-sending expectation, Kiwi uses the KWMessagePattern class, specifically the matchesInvocation: and argumentFiltersMatchInvocationArguments: methods. This checks for three types of "argument filters":
Literal object values (such as myArray in the example above), which are compared to the actual value sent in the message using isEqual:
An object of type KWAny (such as the any() macro in the example above), which will match any argument value
Objects that satisfy [KWGenericMatchEvaluator isGenericMatcher:argumentFilter], which basically means that the object responds to matches:(id)obj
Thus, you should be able to use objects that implement matches: in message-pattern expectations to do things like verify the length of arrays sent to stubbed methods, without resorting to spys or blocks. Here's a very simple implementation: (available as a Gist)
// A reusable class that satisfies isGenericMatcher:
#interface SOHaveCountOfGenericMatcher : NSObject
- (id)initWithCount:(NSUInteger)count;
- (BOOL)matches:(id)item; // this is what KWMessagePattern looks for
#property (readonly, nonatomic) NSUInteger count;
#end
#implementation SOHaveCountOfGenericMatcher
- (id)initWithCount:(NSUInteger)count
{
if (self = [super init]) {
_count = count;
}
return self;
}
- (BOOL)matches:(id)item
{
if (![item respondsToSelector:#selector(count)])
return NO;
return [item count] == self.count;
}
#end
// Your spec:
it(#"should receive an array with count 3", ^{
NSArray* testArray = #[#"a", #"b", #"c"];
id argWithCount3 = [[SOHaveCountOfGenericMatcher alloc] initWithCount:3];
id aMockObject = [SomeObj nullMock];
[[[aMockObject should] receive] doSomething:argWithCount3];
[aMockObject doSomething:testArray];
});
It would be nice to be able to reuse Kiwi's built-in matcher classes here, but I haven't yet found out exactly how to do this.

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;