I want to have a method that will either create a new object or return an existing one based on an identifier string.
This is what I have:
#implementation MyObject {
}
#synthesize data = _data;
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
// these methods are the only ones to be used for managing the MyObject life cycle
+ (NSMutableDictionary *)objectsDict
{
static NSMutableDictionary *map = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
map = [NSMutableDictionary new];
});
return map;
}
+ (MyObject *)getRefrenceForId:(NSString *)identifier
{
return [[MyObject objectsDict] objectForKey:identifier];
}
+ (MyObject *)newRefrenceWithId:(NSString *)identifier
{
MyObject *obj;
#synchronized (self) {
obj = [[MyObject objectsDict] objectForKey:identifier];
if (obj == nil) {
obj = [[MyObject alloc] init];
obj.identifier = identifier;
[[MyObject objectsDict] setObject:obj forKey:identifier];
NSLog(#"new instance of MyObject created with id:%#",identifier);
}
}
return obj;
}
+ (MyObject *)newRefrenceWithId:(NSString *)identifier andClassType:(Class)classType
{
MyObject *obj;
#synchronized (self) {
obj = [[MyObject objectsDict] objectForKey:identifier];
if (obj == nil) {
obj = [[MyObject alloc] initWithClassType:classType andId:identifier];
[[MyObject objectsDict] setObject:obj forKey:identifier];
NSLog(#"new instance of MyObject created with id:%# of ClassType :%#",identifier,NSStringFromClass(classType));
}
}
return obj;
}
+ (void)deleteInstance:(NSString *)identifier
{
#synchronized (self) {
[[MyObject objectsDict] removeObjectForKey:identifier];
}
}
+ (void)clearAllMyObjectsFromMap
{
#synchronized (self) {
[[MyObject objectsDict] removeAllObjects];
}
}
Is there a better way to do this? I hear that #synchronized is very CPU expensive but GCD concurrent queues can't be used in class methods...
UPDATE: Where should the global sync queue be .. in init? That's an instance method so I doesn't work there...
You can use GCD:
Create a global "sync queue" using dispatch_once in a free function defined in module scope:
static dispatch_queue_t get_sync_queue() {
static dispatch_once_t onceToken;
static dispatch_queue_t sync_queue;
dispatch_once(&onceToken, ^{
sync_queue = dispatch_queue_create("my.sync_queue", DISPATCH_QUEUE_CONCURRENT);
});
return sync_queue;
}
Use this queue with dispatch_sync and the block modifying your object:
+ (MyObject *)newRefrenceWithId:(NSString *)identifier
{
__block MyObject *obj;
dispatch_barrier_sync(get_sync_queue(), {
obj = [[MyObject objectsDict] objectForKey:identifier];
if (obj == nil) {
obj = [[MyObject alloc] init];
obj.identifier = identifier;
[[MyObject objectsDict] setObject:obj forKey:identifier];
NSLog(#"new instance of MyObject created with id:%#",identifier);
}
});
return obj;
}
Method newRefrenceWithId: now is fully thread-safe.
Edit:
Alternatively, you can also use a concurrent sync_queue and use dispatch_barrier_sync when reading and writing in the block (as in your case)
Use dispatch_barrier_async when writing the object(s).
In case you just need to read and return the shared object's state you should use dispatch_sync - which allows for concurrent reads.
Related
I have created a singleton:
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
NSAssert(FALSE, #"Please use getSharedInstance class method of MotionManager to avoid singleton abuse. =)");
return nil;
}
+ (id) getSharedInstance
{
if (!instance)
{
instance = [[super allocWithZone:NULL] init];
}
return instance;
}
Why does the above works fine, but the below one throws exception?
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
NSAssert(FALSE, #"Please use getSharedInstance class method of MotionManager to avoid singleton abuse. =)");
return nil;
}
+ (id) getSharedInstance
{
if (!instance)
{
instance = [[super alloc] init];
}
return instance;
}
This is the proper way to create a singleton:
+ (id)sharedManager {
static Singleton *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedManager;
}
This is because alloc is also calling allocWithZone: internally, see NSObject doc
That's the reason why your code instance = [[super allocWithZone:NULL] init]; works while instance = [[super alloc] init]; doesn't.
I've created a singleton class using this code:
static MyClass *sharedMyClass = nil;
+ (id)getInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyClass = [[self alloc] init];
});
return sharedMyClass;
}
my concern is that the users of my class can call the alloc method an create other instantiations of the class. Therefore this would no longer be a singleton Do I need to override the alloc method? If so I suggest overriding it this way:
+ (id)alloc
{
id instance = sharedMyClass;
if (instance == nil) {
instance = [super alloc];
}
return instance;
}
Implement it this way?
+(id)alloc
{ #synchronized([MyClass class])
{
NSAssert(_sharedMyClass == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedMyClass = [super alloc];
return _sharedMyClass;
}
return nil;
}
Yes, you need to overwrite the alloc methode.
You can archive this by implementing
+ (instancetype)sharedInstance {
static MyClass *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[super allocWithZone:NULL] init];
});
return sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
Apple provides a detailed description for this case which you can find here (non ARC description) https://developer.apple.com/legacy/library/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32
You can forbid alloc at compile time:
+ (instancetype)new NS_UNAVAILABLE;
+ (instancetype)alloc NS_UNAVAILABLE;
Using super alloc in your own implementation:
+ (instancetype)getInstance {
static id sharedMyClass
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyClass = [[super alloc] init];
});
return sharedMyClass;
}
It doesn't protect you from some runtime code, but it's clear enough.
I have a Singleton class that has two methods:
- (void)saveString:(NSString *)stringObject {
[[[Singleton sharedInstance] stringArray] addObject:stringObject];
}
- (NSArray *)getArrayContents {
return [[Singelton sharedInstance] stringArray];
}
Here is the implementation code of my Singleton class:
static Singleton *sharedSingleton = nil;
+ (Singleton *) sharedInstance {
if (sharedSingleton == nil) {
sharedSingleton = [[super alloc] init];
}
return sharedSingleton;
}
I have two View Controllers (vcA, and vcB) in my application. What I am trying to do is temporarily store the data from vcA, so that the data inside stringArray will be accessible later to vcB.
Here is the code that vcA uses to store the data:
[[Singleton sharedInstance] saveString:stringName];
Later in the lifecycle of the application, vcB calls the Singleton class to retrieve the values from the NSMutableArray:
NSArray *newArray = [[Singleton sharedInstance] getArrayContents];
for (NSString *test in newArray) {
NSLog(#"Here are the contents of the array %#", test);
}
Unfortunately, when I make the call in vcB to print the contents of the Array, there is no output because the array is empty, despite the fact that values are added to the array. What is it I'm doing wrong?
Try this,
to create Singleton
+(Singleton *)sharedSingleton {
static dispatch_once_t once;
static Singleton *sharedSingleton;
dispatch_once(&once, ^{
sharedSingleton = [[self alloc] init];
});
return sharedSingleton;
}
and the init method of singleton class
- (id)init
{
self = [super init];
if (self) {
//#property stringArray
self.stringArray = [[NSMutableArray alloc] init];
}
return self;
}
Other methods of Singleton
- (void)saveString:(NSString *)stringObject {
[self.stringArray addObject:stringObject];
}
- (NSArray *)getArrayContents {
return self.stringArray;
}
I had this problem. My code in the singleton looked like this:
+ (ReportDataList*)sharedDataArray
{
static dispatch_once_t pred;
static ReportDataList *shared = nil;
dispatch_once(&pred, ^{
shared = [[ReportDataList alloc] init];
self.rDetailsArray = [[NSMutableArray alloc] init];
});
return shared;
}
I had incorrectly initialised the array, so it was emptying it when I created a reference to the singleton later in my code. I removed the array initialisation, which is done in the -(id)init method and it worked fine. So, my code then looked like this:
+ (ReportDataList*)sharedDataArray
{
static dispatch_once_t pred;
static ReportDataList *shared = nil;
dispatch_once(&pred, ^{
shared = [[ReportDataList alloc] init];
});
return shared;
}
- (id)init
{
self = [super init];
if (self) {
self.rDetailsArray = [[NSMutableArray alloc] init];
[self initWithDummyValues];
}else{
NSLog(#"problem initialising array list");
}
return self;
}
First off, these two methods should probably use self, not sharedInstance:
- (void)saveString:(NSString *)stringObject {
[[self stringArray] addObject:stringObject];
}
- (NSArray *)getArrayContents {
return [self stringArray];
}
Second, there’s no point in having a getArrayContents method when you already have stringArray, and get as a prefix is usually reserved for methods that take a parameter to be copied into, anyhow.
Third, I don’t see you initializing stringArray anywhere, so unless there’s code missing, it’s nil and it’s staying nil. Maybe try:
+ (Singleton *) sharedInstance {
if (!sharedSingleton) {
sharedSingleton = [[self alloc] init];
sharedSingleton.stringArray = [NSMutableArray new];
}
return sharedSingleton;
}
Assuming stringArray is declared something like:
#property (readwrite, strong) NSMutableArray *stringArray;
I have created a single ton like this for ARC,
+ (MyClass *)sharedInstance {
static MyClass *sharedSpeaker = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedSpeaker = [[self alloc] init];
});
return sharedSpeaker;
}
- (id)init {
if (self = [super init]) {
}
return self;
}
But here I am creating instances like this:
id speaker3 = [[MyClass alloc] init];
id speaker = [MyClass sharedInstance];
id speaker2 = [[MyClass alloc] init];
NSLog(#"Speaker 1= %# \n speaker 2 = %#\n Speaker3 = %#",speaker,speaker2,speaker3);`
I got output as:
Speaker 1= <MyClass : 0xa45f5e0>
speaker 2 = <MyClass : 0xa461740>
Speaker3 = <MyClass : 0xa4529e0>
This is looking like a desired behaviour. How to stop this when I am giving singleton in library to user. I need to block him from creating new instance. Do I need to make static global if I make it global he cant create the global variable of the same name there will be conflict right. So any memebers can give solution on this?
For example using an assert in the init method.
- (id)init {
static int maxInstances = 1;
assert(maxInstances > 0);
maxInstances--;
...
}
Because you are creating new instances of your singleton class using alloc, init.
You will get the singleton instance using the sharedInstance class method. Like:
id speaker = [MyClass sharedInstance];
If you don't want to create the instances with alloc or init. Override those methods.
You can write static MyClass *sharedSpeaker = nil; as a static global variable and remove it from the sharedInstance method.
in .h file
#import <Foundation/Foundation.h>
#interface Singleton : NSObject
{
///......
}
+ (Singleton *)sharedSingleton;
in .m file
#import "Singleton.h"
#implementation Singleton
static Singleton *singletonObj = NULL;
+ (Singleton *)sharedSingleton
{
#synchronized(self)
{
if (singletonObj == NULL)
singletonObj = [[self alloc] init];
}
return(singletonObj);
}
and use this in another file
#import "Singleton.h"
//.....
Singleton *sinObj = [Singleton sharedSingleton]; // not alloc and init. use direct
create instance like this it will always return you singlton
static testSinglton *myinstance = nil;
+ (testSinglton *) sharedInstance {
if (!myinstance) {
myinstance = [[testSinglton alloc] init];
}
return myinstance;
}
- (id) init {
if (myinstance) {
return myinstance;
}
if (self = [super init]) {
//new object now will be created...
myinstance = self;
}
return self;
}
NSLog(#"%#",[[testSinglton alloc] init]);
NSLog(#"%#",[testSinglton sharedInstance]);
NSLog(#"%#",[[testSinglton alloc] init]);
*I definitely need a break... cause was simple - array was not allocated... Thanks for help. Because of that embarrassing mistake, I flagged my post in order to delete it. I do not find it useful for Users ;) *
I have just tried to create a singleton class in iOS, but I probably I am making a mistake. Code (no ARC is a requirement):
#import "PeopleDatabase.h"
#import "Person.h"
#import <Foundation/Foundation.h>
#interface PeopleDatabase : NSObject{objetive
NSMutableArray* _arrayOfPeople;
}
+(PeopleDatabase *) getInstance;
#property (nonatomic, retain) NSMutableArray* arrayOfPeople;
#end
--
#implementation PeopleDatabase
#synthesize arrayOfPeople = _arrayOfPeople;
static PeopleDatabase* instance = nil;
-(id)init{
if(self = [super init]) {
Person* person = [[[Person alloc] initWithName:#"John" sname:#"Derovsky" descr:#"Some kind of description" iconName:#"johnphoto.png" title:Prof] retain];
[_arrayOfPeople addObject:person];
NSLog(#"array count = %d", [_arrayOfPeople count]); // <== array count = 0
[person release];
}
return self;
}
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil)
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
return(instance);
}
-(void)dealloc {
[instance release];
[super dealloc];
}
#end
When invoking getInstance like here:
PeopleDatabase *database = [PeopleDatabase getInstance];
NSLog(#"Adress 2: %p", database);
Address 2 value the same value as in getInstance.
The standard way of creating a singleton is like...
Singleton.h
#interface MySingleton : NSObject
+ (MySingleton*)sharedInstance;
#end
Singleton.m
#import "MySingleton.h"
#implementation MySingleton
#pragma mark - singleton method
+ (MySingleton*)sharedInstance
{
static dispatch_once_t predicate = 0;
__strong static id sharedObject = nil;
//static id sharedObject = nil; //if you're not using ARC
dispatch_once(&predicate, ^{
sharedObject = [[self alloc] init];
//sharedObject = [[[self alloc] init] retain]; // if you're not using ARC
});
return sharedObject;
}
#end
Check this apple doc on how to create singleton instance:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html
#synchronized(self)
{
if (instance == nil)
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
You appear to be missing your braces for that if statement. As written, the only thing you do different when instance == nil is emit a log message.
After web reading and personal practicing, my current singleton implementation is:
#interface MySingleton
#property myProperty;
+(instancetype) sharedInstance;
#end
#implementation MySingleton
+ (instancetype) sharedInstance
{
static dispatch_once_t pred= 0;
__strong static MySingleton *singletonObj = nil;
dispatch_once (&pred, ^{
singletonObj = [[super allocWithZone:NULL]init];
singletonObj.myProperty = initialize ;
});
return singletonObj;
}
+(id) allocWithZone:(NSZone *)zone
{
return [self sharedInstance];
}
-(id)copyWithZone:(NSZone *)zone
{
return self;
}
this is a thread safe implementation and avoids the risk to create new objects by calling "alloc init" on your class. Attributes initialization has to occur inside the block, not inside "init" override for similar reasons.
This is an error that can be avoided by some disziplined convention which is to always use curly brackets followed by if and else.
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil)
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
return(instance);
}
If instance is nil then the very next statement and only that is executed. And that is the nslog and not the allocation. Then instance is allocated anyway, regardless wether it was used before or not. This will provide you with a new singleton on each call. BTW that causes a leak.
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil) {
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
}
return(instance);
}
But this error came in while debugging. It may confuse you but does not solve your original problem. Please add an alloc and init and retain for _arrayOfPeople as well.
-(id)init{
if(self = [super init]) {
Person* person = [[[Person alloc] initWithName:#"John" sname:#"Derovsky" descr:#"Some kind of description" iconName:#"johnphoto.png" title:Prof] retain];
_arrayOfPeople = [[[NSMutableArray alloc] init] retain]; //dont forget the release
[_arrayOfPeople addObject:person];
NSLog(#"array count = %d", [_arrayOfPeople count]); // <== array count = 1 !!!
[person release];
}
return self;
}
In your code _arrayOfPeople is nil and addObject is sent to nil which does not cause an abort but does not do anything either. Then count is sent to nil wich returns 0/nil.
in this function +(PeopleDatabase *)getInstance i think you need to place curly Braces correctly : like this
+(PeopleDatabase *)getInstance {
#synchronized(self)
{
if (instance == nil)
{
NSLog(#"initializing");
instance = [[[self alloc] init] retain];
NSLog(#"Address: %p", instance);
}
return instance ;
}
}