suppose I got a singleton class MySingleton as coded below.
Now is a singleton class just like any other class. I can have instance variables that are nonatomic and retain?
I can have: #property (nonatomic, retain) NSString* instanceVar in the .h file
and #synthesize instanceVar in the .m file?
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
[[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([MySingleton class])
{
NSAssert(_sharedMySingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMySingleton = [super alloc];
return _sharedMySingleton;
}
return nil;
}
Yes, the instance of a singleton class behaves the same as a standard class, there is just one instance.
The pattern you have is overly complicated, there is no need for +(id)alloc
Here is a simplier pattern:
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(MySingleton*)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
_sharedSingleton = [[MySingleton alloc] init];
}
return _sharedMySingleton;
}
You bet. To the rest of your application, your singleton looks and works just like any other class. The only difference is that when your application tries to create a new singleton it always receives back the same object. But the singleton can have instance methods and instance variables just like any other class.
Not familiar with the annotations you mentioned because I'm a C++ developer, but a singleton can certainly have instance data. That's one of its values.
Yes you can have instance variables, a singleton is simply a regular class, where there is only one instance at any given time.
Related
This question already has answers here:
Using a singleton to create an array accessible by multiple views
(2 answers)
Closed 9 years ago.
I have created a singleton class in Objective C. Like the one below:
#interface SingletonClass : NSObject{
NSMutableArray *instanceArray;
}
#property(nonatomic,retain)NSMutableArray *instanceArray;
+(SingletonClass*)sharedInstance;
#end
#implementation SingletonClass
#synthesize instanceArray;
static SingletonClass *sharedInstance =nil;
+(SingletonClass*)sharedInstance
{
if(sharedInstance==nil){
sharedInstance=[[super allocWithZone:NULL]init];
}
return sharedInstance;
}
-(id)init{
self=[super init];
if(self){
instanceArray=[[NSMutableArray alloc]init];
[instanceArray addObject:#"One"];
[instanceArray addObject:#"Two"];
}
return self;
}
+(id)allocWithZone:(NSZone *)zone{
return [self sharedInstance];
}
#end
I know it can be accessed from anywhere with the following piece of code:
SingletonClass *singletonObject=[SingletonClass sharedInstance];
And the instanceArray can be accessed anywhere by singletonObject.instanceArray.
Now my question is, is is possible to modify the array by adding new objects to it ? Will that be persisted ? Because i tried to add an object from one class
[singletonObject.instanceArray addObject:#"Three"];
When i checked the array contents from another class, the array consisted of only two values which are initialized by default. The value which i added from another class didnt show up.
What could be the problem ? Am I missing something here ? Please help.
Drop the allocWithZone: implementation entirely. It is prone to error and a distinctly odd thing to do. The reality is that if you have code that is using your singleton class and not going through sharedInstance then that code is broken. Attempting to hide that brokenness is just going to cause pain later.
Just do this one:
+(instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SingletonClass alloc] init];
});
return sharedInstance;
}
If you aren't seeing updates to the array in your other class, then there is some other bug.
Your implementation of allocWithZone: is never called because you are calling the [super allocWithZone:], also i don't think you need this method.
i would change your class method with this:
+(instancetype)sharedInstance
{
if(sharedInstance==nil){
sharedInstance = [[SingletonClass alloc]init];
}
return sharedInstance;
}
and if you want to be more secure that you are not going to create another instance of your object use dispatch_once:
+(instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SingletonClass alloc] init];
}
return sharedInstance;
}
I think that you forgot the ! before self in init method :
-(id)init {
self = [super init];
if(!self) {
instanceArray=[[NSMutableArray alloc]init];
[instanceArray addObject:#"One"];
[instanceArray addObject:#"Two"];
}
return self;
}
That's why each time you get a new instance of your singleton
I need to create an object for example NSString and let other classes get/set the value for it.
thx. for help :)
The easiest way to do this is to attach the value as a property to a singleton instance of some class. One singleton instance that already exists in your application is the application delegate. So, just add an NSString property to your application delegate and you can access it from any class in your app (as long as you #import your application delegate).
In your application delegate:
#property(nonatomic, strong) NSString* someString;
In your other classes:
[self doSomethingWithAString:((YourAppDelegateClass*)[[UIApplication sharedApplication] delegate]).someString];
Create a Singleton/Shared class.
#implementation SINGLETON
static SINGLETON *instance = nil;
+(SINGLETON*)sharedInstance{
#synchronized(self) {
if (instance == nil) {
instance = [[SINGLETON alloc] init];
}
return instance;
}
}
EDIT:
This will come handy... Objective-C Singleton problem. Object recognized in one class but not another
Normally, you define init or initWith... methods and call them inside convenient constructors like this:
#implementation MyClass1 : NSObject
#synthesize n, s;
-(id)init
{
self = [super init];
if (self) {
self.n = 1;
}
return self;
}
-(id)initWithString:(NSString *)s
{
self = [self init];
if (self) {
self.s = s;
}
return self;
}
+(id)myClass
{
return [[self alloc] init];
}
+(id)myClassWithString:(NSString *)s
{
return [[self alloc] initWithString:s];
}
#end
But I think it is possible to define convenient constructors without defining init or initWith... methods like this:
#implementation MyClass2
#synthesize n, s;
+(id)myClass
{
MyClass2 *obj = [[self alloc] init];
obj.n = 1;
return obj;
}
+(id)myClassWithString:(NSString *)s
{
MyClass2 *obj = [self myClass];
obj.s = s;
return obj;
}
#end
Is it bad practice to define convenient constructors without defining init method?
If it is bad practice, could you tell me the disadvantage or problems?
I'm not sure if it's actually a bad practice. Generally, when I write convenience constructors they look like this:
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
{
FSClub *club = [[FSClub alloc] init];
if (club)
{
club.identifier = [element integerValueWithPath:#"id"];
club.name = [element valueWithPath:#"naam"];
club.referer = [element URLWithPath:#"referer"];
}
return club;
}
The code still takes into account possible memory issues (initialisation failure) like in a 'normal' init constructor. Values will only be set if initialisation is successful.
The interface file is defined as such:
#interface FSClub : NSObject
#property (nonatomic, assign, readonly) NSInteger identifier;
#property (nonatomic, copy, readonly) NSURL *referer;
#property (nonatomic, copy, readonly) NSString *name;
+ (FSClub *)clubWithXMLElement:(SMXMLElement *)element;
#end
Please note the properties are readonly. I prefer creating immutable objects, since they're easier to deal with in e.g. threaded code. The only way to have the properties set in this situation is by using the convenience constructor.
When I create the convenience constructors, these are generally the only methods I use to instantiate the objects. That is to say, the -init method will not be used most of the time. Also, writing lots of initialisers even if you don't use them takes lots of developer time. I wouldn't create methods that I don't use.
When you create code as part of a framework -code that you share with lots of people that you don't know- in such situations you might want to write both the convenience constructors as well as all the normal constructors, because you can't be sure how the code will be used in people's own project. For example creating lots of objects using convenience constructors in tight loops might be bad for performance, since the objects are added to the autorelease pool. I think this is also true in an ARC scenario. In such situations one might have the option to use 'normal' constructors to create objects.
//I think it is BAD. First of all, you confused CLASS and OBJECT, you get an object like this
// [[CLASSNAME alloc] init];
//not like this:
// [[OBJECT alloc] init];
//so, what you want to do shall be like this:
// +(id)myClass
// {
// MyClass2 *obj = [[[self class] alloc] init];
// obj.n = 1;
// return obj;
// }
PLEASE ignore above all, thanks to #W'rkncacnter.
however, here you are returning an object owned by yourself without autorelease, it's not recommended.
Maybe what you really want is something like factory method?
+(id)myObjectWithString:(NSString *)string
{
MyClass2 *obj = [[[MyClass2 alloc] init] autorelease];
obj.s = string;
return obj;
}
Doing it like you did with MyClass1 makes it easier to define a designated initializer. Apple recommends these; they help reducing code repetition.
I want to have one object that is initialized in the delegate and I want to be able to use this object anywhere across view controllers (doesn't depend on what view I am currently at). I am guessing the solution to this would be to have a singleton class, so far I have the following:
#interface LocationManager : NSObject <CLLocationManagerDelegate>{
NSDate *enter;
NSDate *exit;
CLLocationManager * manager;
}
#property (nonatomic, retain) NSDate * enter;
#property (nonatomic, retain) NSDate * exit;
- (BOOL)registerRegionWithLatitude:(double)latitude andLongitude:(double)longitude;
+ (LocationManager *)instance;
#end
#import "LocationManager.h"
#implementation LocationManager
#synthesize enter;
#synthesize exit;
#pragma mark - CLLocationManager delegate
static LocationManager *gInstance = NULL;
+ (LocationManager *)instance
{
#synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
#end
Is this correct? So all I need to do to access this is just to call instance? Inside LocationManager I also want to have only one CLLocationManager, called manager.. however, where do I initialize it so I only have one? Can I do the following? Most other singleton examples doesn't have any variables in the class, so that's where I got confused
+ (LocationManager *)sharedLocationManager
{
#synchronized(self)
{
if (lm == NULL){
lm = [[self alloc] init];
lm.manager = [[CLLocationManager alloc] init];
lm.manager.delegate = lm;
}
}
return(lm);
}
Basically -- yes.
Just a couple of small things:
static LocationManager *gInstance = NULL;
instead of NULL, you should use nil, it's a convention in Objective-C.
You should also overwrite alloc, new, copyWithZone:, and mutableCopyWithZone:. From Buck/Yacktman: "Cocoa Design Patterns", p. 153:
+ (id)hiddenAlloc
{
return [super alloc];
}
+ (id)new
{
return [self alloc];
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone *)zone
{
[self retain];
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [self copyWithZone:zone];
}
This way, your singleton object cannot be copied. You need to call hiddenAlloc from your instance method (by the way, the method to access a Singleton object is often called sharedInstance in Objective-C).
For other singleton styles with their pros and cons, check out this question.
Personally, I prefer this style (copied from one of the answers on that link):
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
In fact, there's a tried-and-true method to create singletons already. Download the SynthesizeSingleton.h file (from a Cocoa with Love article). It contains a massive amount of pre-processor code which will generate any singleton for you. Hit the article for more details.
Since the factory method "instance" is a class-level method, the #synchronized block should be
#synchronized([LocationManager class]) {
//}
I have create a SinglestonClass in my code but i have a problem.
My variable are initialized in the -init method but when i call the singlestonClass these variable are re-initialize.
Can you help me for create a single initialization for my variable?
thanks.
#implementation SingletonController
#synthesize arrayPosition;
#synthesize arrayMovement;
#synthesize actualPosition;
#synthesize actualMove;
#synthesize stopThread;
+(SingletonController*)sharedSingletonController{
static SingletonController *sharedSingletonController;
#synchronized(self) {
if(!sharedSingletonController){
sharedSingletonController = [[SingletonController alloc]init];
}
}
return sharedSingletonController;
}
//I don't want a re-initialization for these variables
-(id)init{
self = [super init];
if (self != nil) {
arrayPosition = [[NSMutableArray alloc]init];
arrayMovement = [[NSMutableArray alloc]init];
actualPosition = [[Position alloc]init];
actualMove = [[Movement alloc]init];
stopThread = FALSE;
}
return self;
}
-(void) dealloc {
[super dealloc];
}
#end
Your init method should not be called by anyone except for your singleton class itself. That's what the sharedSingletonController method is for. This is your factory method that is responsible for returning the same static instance of your class. I'd also suggest that you rename the static instance of your singleton object and/or the sharedSingletonController selector itself to disambiguate between the two and for cleaner design. In this particular case, it may confuse someone who has to read your code.
Without seeing how the client code is calling on your singleton factory method, it's difficult to decipher where your problem is. We'd need to see the rest of the code including how it's being called. In your client code, you should be using something such as:
SingletonController *sigController = [SingletonController sharedSingletonController];
DO NOT DO:
SingletonController *sigController = [[SingletonController alloc] init];
Read here for more information in the Cocoa Fundamentals Guide.