Cocoa -- change class of an object in app update? - iphone

I have two objects of class WidgetClass in my stored model. They are saved each time the app exits and reloaded each time it starts. I want to update my model to make one of them a WidgetSubclass object. WidgetSubclass will be a subclass of WidgetClass.
WidgetClass has quite a lot of ivars. WidgetSubclass will add few or none.
What is the most efficient way to accomplish the update? I am not using core data.

Couple of things.
If the subclass does not add any ivars to the superclass, you can actually get away with the following:
WidgetSubclass* widget = (WidgetSubclass*)[[WidgetClass alloc]initWithCoder: someCoder];
Class object_setClass(widget, [WidgetSubclass class]);
There is some risk that changes in the runtime could break the above code. So here is a safer way:
Foo.m:
-(void) copyIvarsTo: (Foo*) foo {
[super copyIvarsTo: foo];
foo.ivar1 = [self.objectIvar1 copy];
foo.ivar2 = [self.objectIvar2 copy];
foo.floatIvar = self.floatIvar;
// etc. Method works fine if foo is actually a member of a subclass.
}
-(Foo*) copy {
Foo* clone = [[self class]alloc];
[self copyIvarsTo: clone];
return clone;
}
Now I can have the following NSObject category method:
-(NSObject*) wj_copyWithSubclass: (Class) subclass {
if (![self respondsToSelector: #selector(copyIvarsTo:)])
return nil;
NSAssert([subclass isSubclassOfClass: [self class]], #"call copyWithSubclass only on subclasses");
NSObject* clone = [subclass alloc];
[self copyIvarsTo: clone];
return clone; // at this point, clone has copied all the ivars that are members of the receiver's class. Other ivars have their default values. Calling code needs to handle that.
}

Related

Initializing a static singleton object with NSCoder

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.

shared method to update Core Data NSManagedObject?

I am looking for a basic pattern where I can move some shared code. I have an NSManagedObject PurchaseOrder which is stored in Core Data. This can be edited and changed in several different views. Most of the time it is always the same type of change, PurchaseOrder is updated with data from another NSManagedObject Client.
I am wanting to move this type of code to a single place so I can call something like:
-(void) updatePurchaseOrder: (PurchasOrder *) aPurchaseOrder withClient:(Client *) aClient withManagedObjectContext: (NSManagedObjectContext *) aManagedObjectContext {
// update code goes here
}
Is this a bad idea or a recipe for disaster? I do have a shared instance class I am using already (small bit of bit below):
static SharedFunctions* singletonInstance = nil;
-(id)init
{
if ((self = [super init]))
{
}
return self;
}
+(SharedFunctions*)sharedInstance
{
#synchronized(self) {
if (singletonInstance == nil)
{
singletonInstance = [[SharedFunctions alloc] init];
}
}
return singletonInstance;
}
Which I call function like this:
[[[SharedFunctions] sharedInstance] myMethod];
This has been working well for me. Thoughts?
Where is your updatePurchaseOrder method defined? In this situation, I would create a custom PurchaseOrder subclass of the NSManagedObject and implement an updateWithClient method.
#interface PurchaseOrder : NSManagedObject {
}
-(void)updateWithClient:(Client *)client;
#end
So you can simply call [aPurchaseOrder updateWithClient:aClient]; where you need to. Seems more OO, unless I'm missing something?
If you've not made a custom subclass for PurchaseOrder, you could do the same thing in a category of NSManagedObject.

Allocating / retaining model (mvc)?

I am curious if the code I am using here is a good way of doing this. Basically I am creating a simple model object (in the MVC sense) by lazy instanciating it from within my ViewController. The idea being that the first time I call [[self dataModel] doSomething]; it creates a new (or reuses an existing) object. I was specifically concerned with setting the property and correctly releasing the local alloc, any comments would be much appreciated.
// INTERFACE
DataModel *dataModel;
#property(nonatomic, retain) DataModel *dataModel;
// IMPLEMENTATION
#synthesize dataModel;
// Lazy Instanciation ...
- (DataModel *)model {
if(!dataModel) {
DataModel *tempDataModel = [[DataModel alloc] init];
[self setDataModel:tempDataModel;
[tempDataModel release];
}
return dataModel;
}
// Clean up
- (void)dealloc {
[dataModel release];
[super dealloc];
}
OR: (although I don't really like this as it looks confusing to me)
// Lazy Instanciation ...
- (DataModel *)model {
if(!dataModel) {
[self setDataModel:[[DataModel alloc] init]];
[dataModel release];
}
return dataModel;
}
gary.
The first one is better, and fairly standard and proper. The second form is general bad form. Because it's completely unclear the object you init is the same one that you release. It's always best to think like this:
instiate object
do something with object
release object
In this case "do something with object" happens to be assigning it to a different instance variable. But it really doesn't matter what you do there, the pattern holds true. It's entirely up to the setDataModel: method what happens to the object afterward.
Personally though, I prefer the autorelease for most of these cases. Simply because you have to think about it far less.
DataModel *tempDataModel = [[[DataModel alloc] init] autorelease];
[self setDataModel:tempDataModel];
So the easier pattern to remember is:
instantiate and autorelease object
do something with object
But this is a matter of taste, and many prefer the explicit release.
The first way is clearer, although the effect is the same. Note, however, that if the idea of making dataModel a property and using the accessor's retain mechanism is to keep that all in one place the direct use of the dataModel ivar is breaking that encapsulation.
Actually, there seems to be a bit of a mix-up between this model method and the dataModel property. It would be better to restructure things so that everything is in the dataModel accessor itself, avoiding this slightly awkward redirect:
- (DataModel*) dataModel
{
if ( ! dataModel )
{
// direct ivar access is legit inside the accessor itself
// (at least, I would say so -- no doubt others will disagree!)
dataModel = [[DataModel alloc] init];
}
return dataModel;
}
You might consider overriding the dataModel synthesized getter. Otherwise, it could be a bit confusing with both the dataModel getter and the model messages.
I think you could override your synthesized getter like this:
- (DataModel *)dataModel {
if(!dataModel) {
dataModel = [[DataModel alloc] init];
}
return dataModel;
}

Apple Singleton example query?

I am a little confused by this snippet of code (presented in the CocoaFundamentals guide) that overrides some of the methods when creating a singleton instance.
static id sharedReactor = nil;
+(id)sharedInstance {
if(sharedReactor == nil) sharedReactor = [[super allocWithZone:NULL] init];
return sharedReactor;
}
.
+(id)allocWithZone:(NSZone *)zone {
return[[self sharedInstance] retain];
}
-(id)retain {
return self;
}
In the code where the singleton instance is created the +sharedInstance method calls [super allocWithZone:NILL] from the superclass (which in my case is NSObject) The allocWithZone above is only called if you attempt to use it to create a new singleton.
The bit I am confused about is the use of retain, especially seeing as retain is also overridden to return self. Can anyone explain this, could it not be written:
+(id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
-(id)retain {
return self;
}
EDIT_001:
Based on comments and reading various posts on the web I have decided to go with the following (see below) I have chosen to go for a shared singleton approach where if needed I would have the option of creating a second or third instance. Also at this stage as I am only using the singleton for the model portion of MVC for a simple iPhone app I have decided to leave thread safety out. I am aware its important and as I get more familiar with iPhone programming I will likely use +initialize instead (keeping in mind the subclass issue where it can be called twice) Also I have added a dealloc, firstly to log a message should the singleton be released, but also to clean things up properly should the singleton be no longer required.
#interface SharedManager : NSObject
+(id)sharedInstance;
#end
#implementation SharedManager
static id myInstance = nil;
+(id)sharedInstance {
if(myInstance == nil) {
myInstance = [[self alloc] init];
}
return myInstance;
}
-(void)dealloc {
NSLog(#"_deal: %#", [self class]);
[super dealloc];
myInstance = nil;
}
#end
In testing I found that I had a set the static variable to nil in the dealloc or it maintained its pointer to the original object. I was initially a little confused by this as I was expecting the scope of the static to be the instance, I guess its the class instead, which makes sense.
cheers gary
First, don't use this code. There is almost never a reason to do all this for a simple singleton. Apple is demonstrating a "Forced Singleton," in that it is impossible to create two of them. It is very rare to really need this. You can almost always use the "shared singleton" approach used by most of the Cocoa objects that have a singleton constructor.
Here's my preferred way of implementing shared singleton:
+ (MYManager *)sharedManager
{
static MYManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[self alloc] init];
}
return sharedManager;
}
That's it. No other code is required. Callers who use +sharedManager will get the shared instance. Callers who call +alloc can create unique instances if they really want to. This is how such famous "singletons" as NSNotificationCenter work. If you really want your own private notification center, there is no reason the class should forbid it. This approach has the following advantages:
Less code.
More flexible in cases where a non-shared instance is useful.
Most importantly: the code does what it says it does. A caller who thinks he's making a unique instance with +alloc doesn't encounter surprising "spooky action at a distance" behavior that requires him to know an internal implementation detail of the object.
If you really need a forced singleton because the object in question maps to a unique resource that cannot be shared (and it's really rare to encounter such a situation), then you still shouldn't use +alloc trickery to enforce it. This just masks a programming error of trying to create a new instance. Instead, you should catch the programming error this way:
+ (MYManager *)sharedManager
{
static MYManager *sharedManager = nil;
if (sharedManager == nil)
{
sharedManager = [[self alloc] initSharedManager];
}
return sharedManager;
}
- (id)init
{
NSAssert(NO, #"Attempting to instantiate new instance. Use +sharedManager.");
return nil;
}
// Private method. Obviously don't put this in your .h
- (id)initSharedManager
{
self = [super init];
....
return self;
}
There is a good example of different singleton methods with comments here on SO:
What does your Objective-C singleton look like?
If it helps, the example has a different approach to allocWithZone: which returns nil.

Objective-C/iPhone Memory Management Static Variables

I have a static method that creates an instance of the class and puts it in the static variable. I am wondering what the proper way of memory management is in this situation.
You can't put it in the dealloc-method, because although it can access the static variable any instance method that is created that get's released will also release the sharedInstance.
I guess there might be an option of creating a static destroy method which will manualy release the memory and can be called by the user from appWillTerminate, but that seems a bit odd.
So, again, the question:
What is the proper way of releasing a static variable?
// MyClass.m
#import "MyClass.h"
static MyClass *myClass; // How to properly do memory management
#implementation MyClass
+ (MyClass *)sharedMyClass {
if (myClass == nil) myClass = [[MyClass alloc] init];
return myClass;
}
#end
You can either not release them, which is fine since the app is shutting down anyway. Cocoa on the iPhone already does this, it doesn't completely delete everything, it just lets the app get blown away.
Or you can delete it from appWillTerminate or some other shutdown function.
You'll want to have a look at "Creating a Singleton" on the iPhone dev center to see how to properly implement that pattern. You won't be releasing your singleton, just letting it die when the application exits.
Also, if you're multithreaded you'll probably want to wrap that alloc in a #synchronize( self ) {}
Here is the full text:
Some classes of Foundation and the
Application Kit create singleton
objects. In a “strict” implementation,
a singleton is the sole allowable
instance of a class in the current
process. But you can also have a more
flexible singleton implementation in
which a factory method always returns
the same instance, but you can
allocate and initialize additional
instances.The NSFileManager class fits
this latter pattern, whereas the
UIApplication fits the former. When
you ask for an instance of
UIApplication, it passes you a
reference to the sole instance,
allocating and initializing it if it
doesn’t yet exist.
A singleton object acts as a kind of
control center, directing or
coordinating the services of the
class. Your class should generate a
singleton instance rather than
multiple instances when there is
conceptually only one instance (as
with, for example, NSWorkspace). You
use singleton instances rather than
factory methods or functions when it
is conceivable that there might be
multiple instances one day.
To create a singleton as the sole
allowable instance of a class in the
current process, you need to have an
implementation similar to Listing
2-15. This code does the following:
Declare a static instance of your
singleton object and initialize it to
nil. In your class factory method for
the class (named something like
“sharedInstance” or “sharedManager”),
generate an instance of the class but
only if the static instance is nil.
Override the allocWithZone: method to
ensure that another instance is not
allocated if someone tries to allocate
and initialize an instance of your
class directly instead of using the
class factory method. Instead, just
return the shared object. Implement
the base protocol methods
copyWithZone:, release, retain,
retainCount, and autorelease to do the
appropriate things to ensure singleton
status. (The last four of these
methods apply to memory-managed code,
not to garbage-collected code.)
Listing 2-15 Strict implementation of
a singleton static MyGizmoClass
*sharedGizmoManager = nil;
+ (MyGizmoClass*)sharedManager {
if (sharedGizmoManager == nil) {
sharedGizmoManager = [[super allocWithZone:NULL] init];
}
return sharedGizmoManager; }
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedManager] retain]; }
- (id)copyWithZone:(NSZone *)zone {
return self; }
- (id)retain {
return self; }
- (NSUInteger)retainCount {
return NSUIntegerMax; //denotes an object that cannot be released }
- (void)release {
//do nothing }
- (id)autorelease {
return self; }
If you want a singleton instance (created and
controlled by the class factory
method) but also have the ability to
create other instances as needed
through allocation and initialization,
do not override allocWithZone: and the
other methods following it as shown in
Listing 2-15.
UPDATE: There is now a much easier way to create a singleton
+ (MyClass*)sharedInstance
{
static MyClass* _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[MyClass alloc] init];
});
return _sharedInstance;
}
Using this new style you don't need to worry about the #syncronize or overriding the memory management methods.
static variable or class remains in memory Untill lifetime of your application
So if it is UnUsed then make
Your_variable = nil;
while declaring use static _datatype variable = nil;
which help while initializing .. and memory management
///**************************
// MyClass.m
#import "MyClass.h"
static MyClass *myClass = nil;
#implementation MyClass
+ (MyClass *)sharedMyClass {
if (myClass == nil)
myClass = [[MyClass alloc] init];
return myClass;
}
#end