I have an object that I alloc/init like normal just to get a instance. Later in my application I want to load state from disk for that object. I figure I could unarchive my class (which conforms to NSCoding) and just swap where my instance points to. To this end I use this code...
NSString* pathForDataFile = [self pathForDataFile];
if([[NSFileManager defaultManager] fileExistsAtPath:pathForDataFile] == YES)
{
NSLog(#"Save file exists");
NSData *data = [[NSMutableData alloc] initWithContentsOfFile:pathForDataFile];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
[data release];
Person *tempPerson = [unarchiver decodeObjectForKey:#"Person"];
[unarchiver finishDecoding];
[unarchiver release];
if (tempPerson)
{
[self release];
self = [tempPerson retain];
}
}
Now when I sprinkled some NSLogs throughout my application I noticed
self.person: <Person: 0x3d01a10> (After I create the object with alloc/init)
self: <Person: 0x3d01a10> (At the start of this method)
tempPerson: <Person: 0x3b1b880> (When I create the tempPerson)
self: <Person: 0x3b1b880> (after i point self to the location of the tempPerson)
self.person: <Person: 0x3d01a10> (After the method back in the main program)
What am I missing?
Don't do this. Besides that it breaks identity rules, you can't change the pointer values other parts of a program hold.
A better approach would be to use the PIMPL idiom: your class holds a pointer to an implementation object and you only swap that.
E.g. something along the lines of this:
#class FooImpl;
#interface Foo {
FooImpl* impl;
}
// ...
- (void)load;
#end
#implementation Foo
- (void)load {
FooImpl* tmp = loadFromDisk();
if (tmp) {
FooImpl* old = impl;
impl = tmp;
[old release];
}
}
#end
self is a function argument to instance methods. Assigning to self is perfectly reasonable, just like assigning values to other function arguments is perfectly reasonable. Since the scope of self is the current function, your code leaks one object and releases another in a way that will most likely cause a crash.
The only time it is meaningful to assign to self is in an init method. Although it is almost never used, init methods are allowed to release self and allocate a new object to return or just return nil. The only reason this works is because the return value is self and callers of init expect to use the return value.
As gf pointed out, the correct approach is a load function that assigns new values to the members of your instance, not that tries to replace the instance.
Related
I'm working on an iPhone app and facing some troubles with my shared singleton class.
I'm using a shared singleton to store two variables
int gameRuns and int totalScore
'gamRuns' just increments every time the user loads the app, and 'totalScore' is obvious :D
the issue is as follows, I load the singleton and init using my own method when the app loads using this code:
+ (SingletonLevelState*)sharedLevelStateInstance {
static SingletonLevelState *sharedLevelStateInstance;
#synchronized(self) {
if(!sharedLevelStateInstance) {
//Init a singleton
sharedLevelStateInstance = [[SingletonLevelState alloc] init];
sharedLevelStateInstance->gameRuns = 1;
sharedLevelStateInstance->totalScore = 0;
}
}
return sharedLevelStateInstance;
}
This is working great as I can reference this class from any other class and always get a pointer to the same object, so this works fine from other objects:
sharedLevelState = [SingletonLevelState sharedLevelStateInstance];
sharedLevelStateInstance.gameRuns++;
Now I added the NSCoder protocol, and added the two methods initWithCoder and encodeWithCoder as follows :
- (void) encodeWithCoder: (NSCoder *)coder
{
//encode level data
[coder encodeInt:self->gameRuns forKey:#"gameRuns"];
[coder encodeInt:self->totalScore forKey:#"totalScore"];
}
- (id) initWithCoder: (NSCoder *) coder
{
if(self = [super init]){
self->gameRuns = [coder decodeIntForKey:#"gameRuns"];
self->totalScore = [coder decodeIntForKey:#"totalScore"];
}
return self;
}
Now when the app loads, I check to see if we already have a saved sate, if it exists, I just unarchive the class with that file, if not, I init that class using my custom method above, then set its defaults, encode it to file so we have a saved state, here's the code:
//Load Level state
sharedLevelStateInstance = [SingletonLevelState sharedLevelStateInstance];
//Check if file is saved
NSFileManager *fm = [[NSFileManager alloc] init];
NSString *gameStatePath = [NSString stringWithString:[self getSavePath]];
if([fm fileExistsAtPath:gameStatePath]){
[self loadState];
sharedLevelStateInstance.gameRuns = sharedLevelStateInstance.gameRuns+1;
NSLog(#"Loaded %d times", [sharedLevelStateInstance gameRuns]);
}
[fm release];
Now the last line in the if statement works perfectly, it increments every time I load the app as expected and I feel really happy lol.
However, the problem arises when I try to get a reference of the singleton in another class by doing the following:
sharedLevelStateInstance = [SingletonLevelState sharedLevelStateInstance];
NSLog(#"Played: %d times", sharedLevelStateInstance.gameRuns);
It always counts back to 1, I know what happens but I'm not sue what's the best way to solve it, when I initWithCoder the singleton, It's not returning a static object, it creates a new one, when I init my sharedLevelStateInstance, it calls my first custom method, initializing it to the defaults hardcoded.
So StackOverflow, can you please help me ?!
I just need to know what's the best way to get a reference to the same object without allocating a new one every time I initWithCoder !
Thanks :)
So, you code should probably look like this:
if(self = [[SingletonLevelState sharedLevelStateInstance] retain])
Which sets the variables of the singleton, and returns the singleton. Be sure to retain the singleton, so that when the NSCoder releases this instance, it doesn't fully deallocate your singleton.
I heard from people that you should use self in viewDidUnload. For instance, this is good:
- (void)viewDidUnload
{
self.object = nil;
self.object2 = nil
}
- (void)viewDidUnload
{
object = nil;
object2 = nil;
}
Is there a difference between the 2? And what is it?
There is a difference. The code that is generated by #synthesize will call release on the references to the objects you have before setting the new value. A call to self.object = nil will effectively both release the reference and set it to nil. Without the self it will just set the reference to nil.
What is almost the same as your second example is this:
- (void)viewDidUnload
{
[object release]; object = nil;
[object2 release]; object2 = nil;
}
Note they are not quite the same - if you had defined a custom getter/setter, or had KVC observers set up around one of those properties the self.object = nil would trigger them, whereas the straight [object release] above would not.
First of all, using self eliminates ambiguity.
- (void)viewDidUnload
{
id object = #"whatever";
object = nil; // This refers to the local variable above
self.object = nil; // This refers to the setter of the ivar belonging to the class
}
Another thing to watch out for is if you have the variables synthesized (meaning if you tell the compiler to generate getter & setter automatically), self.object will invoke the getter/setter, while simply object refers to the actual ivar. To prevent this ambiguity when accessing ivar vs calling the setter, you can write something like this:
self->object = nil; // This refers to the ivar object, not the getter/setter
// Or
#synthesize object = _object; // With this, you refer to the ivar as _object
// Then somewhere else
_object = nil; // This refers to the ivar
object = nil; // Compile error, undeclared identifier
self.object = nil; // This refers to the setter
According to Cocoa coding guidelines, we should avoid using underscores in naming variables though. So personally, if I really need to access the ivar directly (for example, when you are overriding the setter), I prefer using ->.
- (void)setSomeInstance:(SomeClass *)aSomeInstanceValue
{
if (someInstance == aSomeInstanceValue)
{
return;
}
SomeClass *oldValue = someInstance;
someInstance = [aSomeInstanceValue retain];
[oldValue release];
}
ok, so setter should look like. I understand first 3 lines - prevent before situation when new object is the same as the old one.
But what about this line:
SomeClass *oldValue = someInstance;
Why system have to keep address of old object. Why can't it be simply
[someinstance release];
someinstance = [aSomeInstanceValue retain];
Actually - no reason.
It's usually just a choice.
There are three idioms for writing accessors.
Autorelease:
- (void)setFoo:(id)newFoo {
[foo autorelease];
foo = [newFoo retain];
}
Less code to write, but I think autorelease in this case is being lazy.
Retain then release
- (void)setFoo:(id)newFoo {
[newFoo retain];
[foo release];
foo = newFoo;
}
Check first
- (void)setFoo:(id)newFoo {
if ([foo isEqual:newFoo]) {
return;
}
[foo release];
foo = [newFoo retain];
}
The only difference between the last two is that the second checks to see if the new value is different to the current value before trying to set the property. At the cost of an extra if statement. So - if the new value is likely to be the same as the old value, using this construction gives better performance.
Generally, and if you're not using properties for some strange reason, use retain then release, and then if profiling shows that there's a bottleneck - use the check first method.
I would suggest the default retain setter works something like this:
- (void) setFoo:(id) foo {
if ( foo == _foo) return;
[_foo release];
_foo = [foo retain];
}
if you don't check if the old and the new foo are the same, you might end up with a reference to a deallocated object if you for some reason write something like this:
myObject.foo = myObject.foo;
Because the same object would first be released, and then retained. If the myObject is the sole owner, the object would be deallocated after the first release, leaving you with a dangling pointer.
The default retain setter works like that :
- (void)setFoo:(Foo *)aFood
{
if (_foo != nil)
[_foo release];
if (aFood != nil)
_foo = [aFood retain];
}
I initialized a class in my singleton called DataModel. Now, from my UIViewController, when I click a button, I have a method that is trying to access that class so that I may add an object to one of its dictionaries. My get/set method passes back the pointer to the class from my singleton, but when I am back in my UIViewController, the class passed back doesn't respond to methods. It's like it's just not there. I think it has something to do with the difference in passing pointers around classes or something. I even tried using the copy method to throw a copy back, but no luck.
UIViewController:
ApplicationSingleton *applicationSingleton = [[ApplicationSingleton alloc] init];
DataModel *dataModel = [applicationSingleton getDataModel];
[dataModel retrieveDataCategory:dataCategory];
Singleton:
ApplicationSingleton *m_instance;
DataModel *m_dataModel;
- (id) init {
NSLog(#"ApplicationSingleton.m initialized.");
self = [super init];
if(self != nil) {
if(m_instance != nil) {
return m_instance;
}
NSLog(#"Initializing the application singleton.");
m_instance = self;
m_dataModel = [[DataModel alloc] init];
}
NSLog(#"ApplicationSingleton init method returning.");
return m_instance;
}
-(DataModel *)getDataModel {
DataModel *dataModel_COPY = [m_dataModel copy];
return dataModel_COPY;
}
For the getDataModel method, I also tried this:
-(DataModel *)getDataModel {
return m_dataModel;
}
In my DataModel retrieveDataCategory method, I couldn't get anything to work. I even just tried putting a NSLog in there but it never would come onto the console.
Any ideas?
Most likely you are sending messages that get ignored, e.g. they're being sent to objects which don't exist/aren't the one you're looking for, and for some reason aren't crashing. This occurs in the case of messaging nil, or possibly other illegitimate values. Although you seem to expect that the m_ variables will be initialized to 0, this is not good form, and furthermore you are not following a very typical objc pattern for your singletons -- m_dataModel should be an ivar of m_instance, and m_instance should probably be declared static, as you probably don't want it accessed from other files directly. In addition, the most likely source of your bug is somehow the -init method, which should never be called on a singleton -- instead do something like this:
+ (ApplicationSingleton *)sharedInstance {
static ApplicationSingleton *instance = nil;
if(!instance) {
instance = [[self alloc] init]; //or whatever custom initializer you would like, furthermore some people just put the initialization code here and leave -init empty
}
return instance;
}
the code you have now leaks because you allocate an object (self) and don't release it before returning a potentially different instance (the shared one if one already exists), such that the newly allocated one is typically lost.
Let's say I'm building a new class for the iPhone in Objective-C. In one of my init methods I want to manually allocate some memory. So, I might have something like this:
- (id)initWithSomeObject:(SomeObject *)someObject {
self = [super init];
if (self != nil) {
myObject = someObject;
[myObject retain];
if ( (memory = calloc(1, sizeof(SomeStruct)) == NULL) {
// What should I do here to clean up
[self release];
self = nil;
}
}
return self;
}
Now, assuming that the calloc() could fail, and that failing to allocate memory is catastrophic for my object, what should I do inside the if-body to clean up properly? Is there an Objective-C idiom or pattern that I should be using?
Edit: I included the code posted by Rob Napier. But, I still have to release myObject, right? Or does the added code somehow trigger dealloc()?
Yes, you should release yourself and then return nil.
[self release];
self = nil;
See Issues with Initializers in the Concepts in Objective-C Programming guide.
You need to clean up anything you need to and then set the self reference to nil. Apple Dev Portal has an article:
Link
I just tried. -dealloc gets called due to [self release], so myObject would not need to get released in initWithSomeObject. To be sure, you might move myObject = [someObject retain]; (I prefer that style in case -retain might fail for some reason) below the call that might fail (if that's possible).