Objective C - Category to modify a singleton object? - objective-c

I know that the whole point of singleton is to instantiate 1 instance of the onject and reuse it, but when it comes to unit testing I want to be able to renew the singleton object before every test.
I tried to use a category to access the singleton object and release it but It's not accessible by categories any idea what's the best way to achieve this?
#implementation SingletonClass
static SingletonClass *singleton;
+ (SingletonClass*)sharedInstance
{
if (!singleton)
{
singleton = [[SingletonClass alloc] init];
}
return singleton;
}
#end
.
#implementation SingletonClass(Unit Testing Additions)
+ (void)killInstance
{
// I get an error here and I cannot access the singleton Object
[singleton release], singleton = nil;
}
#end

By the very definition of singleton, you can't do this.
If it is your class, don't make it a singleton.
If it isn't your class, doing this will fail.

I'm not sure whether this will work, but maybe you could just override the sharedInstance class method and manage the singleton yourself:
#implementation SingletonClass (Unit Testing Additions)
static SingletonClass *myVeryOwnSharedInstance;
+ (SingletonClass *) sharedInstance
{
if (!myVeryOwnSharedInstance)
myVeryOwnSharedInstance = [[self alloc] init];
return myVeryOwnSharedInstance;
}
+ (void) killInstance
{
[myVeryOwnSharedInstance release];
// if release is overridden to do no-op, maybe just invoke -dealloc directly
myVeryOwnSharedInstance = nil;
}
#end

If you want access to your singleton global variable outside of the file in which it's declared, you'll need to make it globally accessible using extern.
At the top of SingletonClass.h, put this:
extern SingletonClass *singletonClassSingleton;
In your SingletonClass.m, use this:
SingletonClass *singletonClassSingleton = nil;
Then assuming you have #import "SingletonClass.h" in your unit test .m file, you should be able to add:
#implementation SingletonClass(Unit Testing Additions)
+ (void)killInstance
{
[singletonClassSingleton release], singletonClassSingleton = nil;
}
#end
The reason I've renamed singleton to singletonClassSingleton is that the variable is now global - if you have a bunch of singleton classes, you need these variables to have unique names, like dataManagerSingleton, resourceManagerSingleton or whatever.

Here is my own solution.
#implementation SingletonClass(Unit Testing Additions)
//Override
static SingletonClass *singleton;
+ (void)killInstance
{
// I get an error here and I cannot access the singleton Object
[singleton release], singleton = nil;
}
//Override
+ (SingletonClass*)sharedInstance
{
if (!singleton)
{
singleton = [[SingletonClass alloc] init];
}
return singleton;
}
#end

Related

What is a good approach to set init configurables variables on singleton classes in Objective-c?

Nomaly I used to build a singleton the following recipe:
+ (MyClass *)sharedInstance
{
static MyClass *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[self alloc] init];
// Init variables
});
return _sharedInstance;
}
Then I can call methods as following:
[[MyClass sharedInstance] anyInstanceMethod];
But, what happen when any init variables are configurables from outside the class?
My approach is create two class methods, one of them with configurables variables:
+ (MyClass *)sharedInstanceWithVariableOne:(NSString*)aParamOne andVariableTwo:(NSString*)aParamTwo
{
static MyClass *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[self alloc] init];
// Init configurables variables
_sharedInstance.paramOne = aParamOne;
_sharedInstance.paramTwo = aParamTwo;
});
return _sharedInstance;
}
And the second one as proxy to this last one with default values:
+ (MyClass *)sharedInstance
{
return [MyClass sharedInstanceWithVariableOne:#"value1" andVariableTwo:#"value2"];
}
So, if you want to use singleton class with configure variables, you should call first of all sharedInstanceWithVariableOne:andVariableTwo and then only sharedInstance.
I think this approach is not the best and I'm looking forward to use others.
Thanks in advance.
Thanks for your fast responses.
As you says, it's not a singleton.
I'm going to use the same behaviour as [NSURLCache setSharedURLCache:]and [NSURLCache sharedURLCache].
Interface
// MyClass.h
#interface MyClass : NSObject
+ (MyClass *)sharedMyClass;
+ (void)setSharedMyClass:(MyClass *)object;
- (id)initWithParamOne:(NSString)p1 andParamTwo:(NSString)p2;
#end
Implementation
// MyClass.m
#interface MyClass ()
#property(nonatomic, strong) NSString *paramOne;
#property(nonatomic, strong) NSString *paramTwo;
#end;
#implementation MyClass
static MyClass *_sharedInstance = nil;
+ (MyClass *)sharedMyClass
{
if (_sharedInstance == nil) {
_sharedInstance = [[MyClass alloc] initWithParamOne:#"value1" andParamTwo:#"value2"];
}
return _sharedInstance ;
}
+ (void)setSharedMyClass:(MyClass *)object
{
_sharedInstance = object;
}
- (id)initWithParamOne:(NSString)p1 andParamTwo:(NSString)p2
{
self = [super init];
if (self) {
_paramOne = p1;
_paramTwo = p2;
}
return self;
}
Then I could use [[MyClass sharedMyClass] anyMethod]
There are many ways to design what you are after with your shared instance. (It is not a singleton as many comments point out, for that there must be only one instance of the class possible. However that said the shared instance pattern is often used as a "poor man's" singleton with no direct calls to any init methods. Back to the question...) Maybe the following will suit you:
+ (void) setSharedInstanceParameterOne:(NSString*)aParamOne
andParameterTwo:(NSString*)aParamTwo
{
MyClass *_sharedInstance = self.sharedInstance;
_sharedInstance.paramOne = aParamOne;
_sharedInstance.paramTwo = aParamTwo;
}
Now you use [MyClass setSharedInstanceParameterOne:p1 andParameterTwo:p2] to set/change the parameters of the shared instance, and [MyClass sharedInstance] (or MyClass.sharedInstance, though some might have a philosophical discussion about that) to access it.

trying to understand the Singleton concept in objective-c with many variables

I'm trying to understand the Singleton concept in objective-c.
Most examples that I did found just refer to a single variable.
I'm a bit lost about how to adapt the examples to handle many variables as per an example the accelerometer values that return x, y, z.
Can you guide me a bit further ?
A Singleton refers to a special object that can only exist once inside the lifespan of your application. That object can have as many variables and properties as necessary.
// Singleton.h
#interface Singleton : NSObject
#property (readwrite) int propertyA;
#property (readwrite) int propertyB;
#property (readwrite) int propertyC;
+ (Singleton *)sharedInstance;
#end
The key to a Singleton is that it can only be created once. Usually in Objective-C we use the #synchronized() directive to make sure it only gets created once. We put this in a convenience class method called sharedInstance and return our Singleton. Since the Singleton is just an object it can easily have multiple properties, variables, and methods.
// Singleton.m
#import "Singleton.h"
#interface Singleton ()
{
int variableA;
int variableB;
int variableC;
}
#end
#implementation Singleton
static Singleton *sharedInstance = nil;
+ (Singleton *)sharedInstance
{
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[Singleton alloc] init];
}
}
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [super allocWithZone:zone];
return sharedInstance;
}
}
return nil;
}
- (id)init {
self = [super init];
if (self) {
// Inits
}
return self;
}
#end
This is not the ONLY way to create a Singleton. Remember the important part is it can only be created once. So you can take advantage of newer Grand Central Dispatch calls when developing for OSX and iOS such as dispatch_once.
Talking to the Singleton
So lets say you have another object elsewhere talking to the Singleton. This can be done anywhere you #import "Singleton.h"
- (void)someMethod
{
// Setting properties
int valueA = 5;
[[Singleton sharedInstance] setPropertyA:valueA];
// Reading properties
int valueB = [[Singleton sharedInstance] propertyB];
}

Singleton gets deallocated

I've created a simple singleton class to hold static data for my projects.
The first time I access this singleton is onEnter method in my Cocos2d scene. However when I try to access it again later in another method (same scene) this singleton is already released. I'm confused, how do I keep my singleton from being deallocated?
Here's my singleton's interface part:
#import <Foundation/Foundation.h>
#interface OrchestraData : NSObject
+(OrchestraData *)sharedOrchestraData;
#property (retain, readonly) NSArray *animalNames;
#end
Implementation:
#import "OrchestraData.h"
#implementation OrchestraData
#synthesize animalNames = animalNames_;
+(OrchestraData*)sharedOrchestraData
{
static dispatch_once_t pred;
static OrchestraData *_sharedOrchestraData = nil;
dispatch_once(&pred, ^{ _sharedOrchestraData = [[OrchestraData alloc] init]; });
return _sharedOrchestraData;
}
-(id)init {
if (self = [super init]) {
animalNames_ = [NSArray arrayWithObjects:#"giraffe", #"giraffe", #"giraffe", #"giraffe", nil];
}
return self;
}
#end
I'm using my singleton this way:
[[OrchestraData sharedOrchestraData] animalNames];
Update:
I took a fresh look into it with NSZombies enabled, it appears as if my NSArrays were released, not the singleton itself. What do I do?
You must implement your singleton in this way:
1) in .h file of your Singleton Class:
+ (SingletonClass *)instance;
2) in .m file:
+ (SingletonClass *)instance {
static SingletonClass* instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
//your init code here
});
return instance;
}
If you want to call your singleton, just call [SingletonClass instance].
If your are interesting what is "dispatch_once_t", read about Grand Central Dispatch:
http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
RE Update:
Your NSArray deallocates because you're using the autorelease initializer arrayWithObjects and you assign it directly to the ivar animalNames_. Therefore it is not retained.
To fix this, assign the array to the property:
self.animalNames = [NSArray arrayWithObjects:#"giraffe", #"giraffe", #"giraffe", #"giraffe", nil];
Btw, under ARC this wouldn't have been an issue since the ivar would have been a strong (retaining) reference. I don't get tired encouraging anyone to switch to ARC. It's been available for well over a year now and there's absolutely no point in using MRC code anymore! It really pains me to see how developers still don't use the easier, faster, and straightforward option. (rant off) :)
You setup a pointer wherever you need it.
-(void)someMethod {
MySingleton *singleton = [MySingleton sharedSingleton];
singleton.data = YES; //dumb example to show you did something...
}
-(void)someOtherMethod {
MySingleton *singleton = [MySingleton sharedSingleton]; //You have to create a new pointer...
singleton.data = NO; //another dumber example to show you did something...
}
Note: this assumes that you have created a singleton the same way I have... your code might be different therefore causing my answer not to apply...
You need to overwrite the below method inside your singleton class, because in your program, if someone has initialised [[SingletonClass alloc] init] then singleton will have another instance and release it will cause an error.
+ (id)allocWithZone:(NSZone *)zone{
return [[self SingletonClass] retain];
}
- (id)copyWithZone:(NSZone *)zone{
return self;
}
- (id)retain{
return self;
}

Global variable NSMuteableArray using Singleton Class

I'm having trouble creating a nice way of passing a collection around to different view controllers. For example, I created a custom class called Message with a bunch of attributes. I want to have a global NSMutableArray of those stored in a global variable of sorts called messages that I can add to or get from anywhere. Everyone on Stackoverflow says not to use your delagate class to store global variables so I created a singleton class called Shared. In there I created a property for the NSMutableArray called messages like this:
#interface Shared : NSObject {
}
#property (nonatomic, retain) NSMutableArray *messages;
+(Shared *) sharedInstance;
#end
And my .h file is (the important part):
#import "Shared.h"
static Shared* sharedInstance;
#implementation Shared
#synthesize messages;
static Shared *sharedInstance = nil;
-(id) init {
self = [super init];
if (self != nil){
}
return self;
}
-(void) initializeSharedInstance {
}
+ (Shared *) sharedInstance{
#synchronized(self) {
if (sharedInstance == nil){
sharedInstance = [[self alloc] init];
[sharedInstance initializeSharedInstance];
}
return (sharedInstance);
}
}
In my other view controller, I first import "Shared.h", then try this:
[[Shared sharedInstance].messages addObject:m];
NSLog([NSString stringWithFormat:#"Shared messages = %#", [Shared sharedInstance].messages]);
It keeps printing null instead of the the collection of m objects. Any thoughts?
You need to have a static variable.
In .h:
#interface Shared : NSObject
{
NSMutableArray *messages;
}
#property (nonatomic, retain) NSMutableArray *messages;
+ (Shared*)sharedInstance;
#end
in .m:
static Shared* sharedInstance;
#implementation Shared
#synthesize messages;
+ (Shared*)sharedInstance
{
if ( !sharedInstance)
{
sharedInstance = [[Shared alloc] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if ( self )
{
messages = [[NSMutableArray alloc] init];
}
return self;
}
A thought:
#synthesize generates setter and getter methods, it doesn't init your variable. Where do you do that? I can't see it in the excerpts you posted.
The following is not an answer to your issue, but instead a suggestion to an alternative approach that (in my opinion) is 'cleaner' in use.
An alternative to using a Singleton to store app-wide could be to define a class with class methods that retrieves values from the NSUserDefaults. This class could be imported into the prefix header (*.pch) so you can access it from every other class in the project.
Methods inside this class could look like this:
inside Settings.h:
// for this example I'll use the prefix for a fictional company called SimpleSoft (SS)
extern NSString *kSSUserLoginNameKey;
+ (NSString *)userLoginName;
+ (void)setUserLoginName:(NSString *)userLoginName;
inside Settings.m:
kSSUserLoginNameKey = #"SSUserLoginNameKey";
+ (NSString *)userLoginName
{
return [[NSUserDefaults standardUserDefaults] valueForKey:kSSUserLoginNameKey];
}
+ (void)setUserLoginName:(NSString *)userLoginName
{
[[NSUserDefaults standardUserDefaults] setValue:userLoginName forKey:kSSUserLoginNameKey];
[[NSUserDefaults standardUserDefaults] synthesize];
}
Of course in a setup like this NSUserDefaults is the singleton that is being accessed through a convenience class. This class acts as a wrapper around the NSUserDefaults singleton. Values can be accessed like this:
NSString userLoginName = [Settings userLoginName];
[Settings setUserLoginName:#"Bob"];
Other objects -like Arrays- could be accessed in much the same way. One thing to be careful with (much the same as with your current approach) is to be careful not to access a class like this from every other class. Components that are intended to be reusable should pass values, so the components of the application don't become too tightly coupled to the settings class.

Objective-C Proper way to create class with only one instance

I am trying to implement a class, that subclasses NSObject directly, that can only have one instance available throughout the entire time the application using it is running.
Currently I have this approach:
// MyClass.h
#interface MyClass : NSObject
+(MyClass *) instance;
#end
And the implementation:
// MyClass.m
// static instance of MyClass
static MyClass *s_instance;
#implementation MyClass
-(id) init
{
[self dealloc];
[NSException raise:#"No instances allowed of type MyClass" format:#"Cannot create instance of MyClass. Use the static instance method instead."];
return nil;
}
-(id) initInstance
{
return [super init];
}
+(MyClass *) instance {
if (s_instance == nil)
{
s_instance = [[DefaultLiteralComparator alloc] initInstance];
}
return s_instance;
}
#end
Is this the proper way to accomplish such a task?
Thanks
You need to do a little more than that. This describes how an objective-c singleton should be implemented: Objective-C Singleton
In your scenario, there is still a way to create a second instance of your class:
MyClass *secondInstance = [[MyClass alloc] initInstance]; //we have another instance!
What you want is to override your class's +(id)alloc method:
+(id)alloc{
#synchronized(self){
NSAssert(s_instance == nil, #"Attempted to allocate a second instance of singleton(MyClass)");
s_instance = [super alloc];
return s_instance;
}
return nil;
}