i have been using mostly the appDelegate class for global variables but recently i came to know that its not a gud way for keeping global variables so i am trying to make a singleton class as following
#implementation globalVar
static globalVar *_sharedInstance =nil;
#synthesize totalTime;
- (id) init
{
if (self = [super init])
{
}
return self;
}
+ (globalVar *) sharedInstance
{
#synchronized (self) {
if (_sharedInstance == nil) {
[[self alloc] init];
}
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
#synchronized(self) {
if (_sharedInstance == nil) {
_sharedInstance = [super allocWithZone:zone];
return _sharedInstance;
}
}
return nil;
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
-(NSUInteger)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
// do nothing
}
- (id)autorelease
{
return self;
}
- (void) setTotalTime:(NSString *)time
{
#synchronized(self) {
if (totalTime != time) {
[totalTime release];
totalTime = [NSString stringWithFormat:#"%#",time];
}
}
//NSLog(#"time %#",totalTime);
}
-(NSString *)getTotalTime
{
#synchronized(self) {
//NSLog(#"total %#",totalTime);
return totalTime;
}
}
when i set the value for totaltime in my appDelegate class and retrieve it in that class only i get the correct value. but when i only retrieve the value in some other class i get BAD EXCESS. i first create the sharedinstance and then only call this method then why am i getting this error??
globalVar *myEngine = [globalVar sharedInstance];
NSLog(#"about %#",[myEngine totalTime]);
in my app delegate
globalVar *myEngine = [globalVar sharedInstance];
[myEngine setTotalTime:totalTime];
NSLog(#"in app delegate%#",[myEngine getTotalTime]);
You're releasing totalTime but not retaining the new value, which means that when you access it it's already been released, causing a bad access exception.
You can correct this by changing the line where you set the value to include a call to retain:
totalTime = [[NSString stringWithFormat:#"%#",time] retain];
Have a look at the discussion here:
Is it good practice to use AppDelegate for data manipulation and Handling?
Related
In your opinion if I have a singleton subclass of NSObject being initialised with parameters like this:
- (MyObject *) initWithSomeParam:(NSString *)param{
self = [super init];
if (SharedInstance == nil){
SharedInstance = [super init];
SharedInstance.someProperty = param;
}
return self;
}
+ (MyObject *) objectWithSomeParam:(NSString *)param{
return [[self alloc] initWithSomeParam:param];
// Will the alloc cause a leak?
}
The user doesn't have access to the instance method, just the class. Thanks.
That's not the normal way of implementing a singleton and you are breaking the convention of init. Better would be to create a sharedInstance class method and leave the initWithParam method to be more conventional:
static MyObject *_sharedInstance = nil;
+ (MyObject *)sharedInstance:(NSString *)param
{
if (_sharedInstance == nil)
{
_sharedInstance = [MyObject alloc] initWithParam:param];
}
return _sharedInstance;
}
// This must be called during app termination to avoid memory leak
+ (void)cleanup
{
[_sharedInstance release];
_sharedInstance = nil;
}
- (id)initWithParam:(NSString *)param
{
self = [super init];
if (self != nil)
{
self.someProperty = param;
}
return self;
}
However, even that doesn't seem very comfortable; i.e. what happens if the user calls sharedInstance with a different parameter? Perhaps you want to keep a NSMutableDictionary of the initialized objects and create/return them depending on the parameter?
If so, you would do:
static NSMutableDictionary _sharedInstances = [[NSMutableDictionary alloc] init];
+ (MyObject *)sharedInstance:(NSString *)param
{
MyObject *obj = [_sharedInstances objectForKey:param];
if (obj == nil)
{
obj = [[MyObject alloc] initWithParam:param];
[_sharedInstances setObject:obj forKey:param];
}
return obj;
}
// This must be called during app termination to avoid memory leak
+ (void)cleanup
{
[_sharedInstances release];
_sharedInstances = nil;
}
I am trying to create a singleton User class in my app, here's the code:
#import "User.h"
#import "Login.h"
#import "SFHFKeychainUtils.h"
// Constants
static NSString* const kDBUserCurrentUserIDDefaultsKey = #"kDBUserCurrentUserIDDefaultsKey";
// Current User singleton
static User* currentUser = nil;
#implementation User
#synthesize username = _username;
#synthesize password = _password;
#synthesize delegate = _delegate;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (NSString*)primaryKeyProperty {
return #"username";
}
+ (User*)currentUser {
if (nil == currentUser) {
id username = [[NSUserDefaults standardUserDefaults] objectForKey:#"kApplicationUserNameKey"];
if (!username) {
currentUser = [self new];
} else{
NSLog(#"CURRENT USER");
return self;
}
[currentUser retain];
}
return currentUser;
}
+ (void)setCurrentUser:(User*)user {
[user retain];
[currentUser release];
currentUser = user;
}
/**
* Implementation of a RESTful login pattern. We construct an object loader addressed to
* the /login resource path and POST the credentials. The target of the object loader is
* set so that the login response gets mapped back into this object, populating the
* properties according to the mappings declared in elementToPropertyMappings.
*/
- (void)loginWithUsername:(NSString*)username andPassword:(NSString*)password delegate:(NSObject<DBUserAuthenticationDelegate>*)delegate {
_delegate = delegate;
//[RKObjectManager sharedManager].client.username = username;
//[RKObjectManager sharedManager].client.password = password;
self.username = username;
self.password = password;
RKObjectMapping * userMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:#"LoginViewController"];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:#"/account/verify.json" objectMapping:userMapping delegate:self];
}
/**
* Implementation of a RESTful logout pattern. We POST an object loader to
* the /logout resource path. This destroys the remote session
*/
- (void)logout/*:(NSObject<DBUserAuthenticationDelegate>*)delegate */{
NSError * error = nil;
[[NSUserDefaults standardUserDefaults] setValue:nil forKey:#"kApplicationUserNameKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
[SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:#"convore" error:&error];
NSLog(#"LOGGING OUT");
if ([self.delegate respondsToSelector:#selector(userDidLogout:)]) {
[self.delegate userDidLogout:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"DBUserDidLogoutNotification" object:nil];
}
- (void)loginWasSuccessful {
// Upon login, we become the current user
[User setCurrentUser:self];
NSError * error = nil;
// Persist the username for recovery later
[[NSUserDefaults standardUserDefaults] setValue:self.username forKey:#"kApplicationUserNameKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
[SFHFKeychainUtils storeUsername:self.username andPassword:self.password forServiceName:#"convore" updateExisting:TRUE error:&error];
// Inform the delegate
if ([self.delegate respondsToSelector:#selector(userDidLogin:)]) {
[self.delegate userDidLogin:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"DBUserDidLoginNotification" object:self];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
NSLog(#"Loaded payload: %#", [response bodyAsString]);
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
if ([objectLoader wasSentToResourcePath:#"/account/verify.json"]) {
Login * login = (Login *) object;
if ([login.username length] > 0)
[self loginWasSuccessful];
}
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError*)error {
if ([objectLoader wasSentToResourcePath:#"/account/verify.json"]) {
NSLog(#"Encountered an error: %#", error);
// Login failed
if ([self.delegate respondsToSelector:#selector(user:didFailLoginWithError:)]) {
[self.delegate user:self didFailLoginWithError:error];
}
}
}
- (BOOL)isLoggedIn {
return self.username != nil;
//return self.singleAccessToken != nil;
}
- (void)dealloc {
_delegate = nil;
[_password release];
[_passwordConfirmation release];
[super dealloc];
}
#end
The issue is that whenever I tried to access currentUser it always breaks down. I first called the loginWithUsernameandPassword and then tried calling the currentUser, but when I call the currentUser on logout, it gives me an error:
calling this:
if ([[User currentUser] isLoggedIn])
gives me:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[User isLoggedIn]: unrecognized selector sent to class 0x1a671c'
seems that currentUser is nil, why is this?
Quick Singleton 101 (I wish I had this when I started, lol. Everyone just pointed me to the docs which didn't help much). The name of the singleton is going to be "Singleton"
//Singleton.h
#import <Foundation/Foundation.h>
#interface SingletonManager : NSObject
{
NSDictionary* randomDictionary; //just using a dictionary for demonstrative purposes. You can make this a string or whatever you want.
}
+ (Singleton*)sharedSingleton;
#property (nonatomic, retain) NSDictionary *randomDictionary;
#end
And now the .m
//Singleton.m
#import "Singleton.h"
static Singleton *sharedSingleton = nil;
#implementation Singleton
#synthesize randomDictionary;
#pragma mark Singleton Method
+ (Singleton*)sharedSingleton
{
#synchronized(self)
{
if(sharedSingleton == nil)
{
sharedSingleton = [[super allocWithZone:NULL] init];
}
}
return sharedSingleton;
}
#end
And to set/get, first import the singleton in whatever class you need: #import "Singleton.h", then grab the singleton with Singleton *singletonManager = [Singleton sharedSingleton]; and then you can do whatever you need to as necessary. i.e. to get the description of the NSDictionary you would call [[singletonManager randomDictionary] description];
Now this is using ARC, so if you are not you'd just have to make sure you manage your memory correctly. Enjoy.
You need to get the singleton object before you can call a method on it.
if ( [[User currentUser] isLoggedIn] ) {
// Magic happens here
}
You aren't coding your singleton properly.
+ (User *) currentUser {
#synchronized (self) {
if (currentUser == nil) {
currentUser = [[self alloc] init];
}
return currentUser;
}
}
The answer is really a combo of the two answers from XCodeDev and Matthieu Cormier. You need to "protect" your init the way the code sample says so new versions of the object are not created. Otherwise, its not a real singleton. More info on Singleton pattern.
Also, just because its a singleton doesn't mean you can access it with just class methods after you initialize it. You still need to get the instance you initialized, otherwise you cannot do operations that require certain values only in the instance.
I'm doing singleton and A cannot use methods on it... the type for arguments is not right apparently... and there are lots of mistakes (errors)...
Errors are in comment (in the code)
In a foreign Class :
[ [MySingleton sharedMySingleton] setAuth:#"test"]; //incompatible type for argument 1 of setAuth
NSLog([ [MySingleton sharedMySingleton] getAuth]); //Incompatible type for argument 1 os NSLOG
In my singleton's Classes :
#import <Foundation/Foundation.h>
#interface MySingleton : NSObject {
NSString *myToken;
}
+(MySingleton*)sharedMySingleton;
-(void)setAuth:(NSString) token;
-(NSString)getAuth;
#property (nonatomic, retain) NSString *myToken;
#end
... and ...
#import "MySingleton.h"
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
#synthesize myToken;
+(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;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
}
return self;
}
-(void)setAuth:(NSString) token { // Can not use as abject as parameter to a method
myToken=token;// incomatible types in assignment
}
-(NSString)getAuth { // can not use an object as parameter to a method
return myToken;// incomatible types in return
} // control reaches end of non-void function
#end
-(void)setAuth:(NSString) token;
-(NSString)getAuth;
should be
-(void)setAuth:(NSString*) token;
// ^ note a pointer is needed
-(NSString*)getAuth;
// ^ note a pointer is needed
Also, your alloc is crazy. You actually invoke [super alloc] which will give you an object of the wrong type. Check out how to implement singleton in objective-c for iphone for better ways to do singletons.
Your header file is missing the * for the NSString type. For example, it should be :
-(void)setAuth:(NSString *) token;
Replace NSString with NSString* everywhere.
I made this class that turns any object into a singleton, but I know that it's not "concurrent queue safe." Could someone please explain to me how to do this, or better yet, show me the code. To be clear I want to know how to use this with operation queues and dispatch queues (NSOperationQueue and Grand Central Dispatch) on iOS.
Thanks in advance,
Rich
EDIT: I had an idea for how to do it. If someone could confirm it for me I'll do it and post the code. The idea is that proxies make queues all on their own. So if I make a mutable proxy (like Apple does in key-value coding/observing) for any object that it's supposed to return, and always return the same proxy for the same object/identifier pair (using the same kind of lazy loading technique as I used to create the singletons), the proxies would automatically queue up the any messages to the singletons, and make it totally thread safe.
IMHO this seems like a lot of work to do, so I don't want to do it if it's not gonna work, or if it's gonna slow my apps down to a crawl.
Here's my non-thread safe code:
RMSingletonCollector.h
//
// RMSingletonCollector.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "RMWeakObjectRef.h"
struct RMInitializerData {
// The method may take one argument.
// required
SEL designatedInitializer;
// data to pass to the initializer or nil.
id data;
};
typedef struct RMInitializerData RMInitializerData;
RMInitializerData RMInitializerDataMake(SEL initializer, id data);
#interface NSObject (SingletonCollector)
// Returns the selector and data to pass to it (if the selector takes an argument) for use when initializing the singleton.
// If you override this DO NOT call super.
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier;
#end
#interface RMSingletonCollector : NSObject {
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier;
+ (void)destroyCollection;
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier;
#end
// ==--==--==--==--==Notifications==--==--==--==--==
extern NSString *const willDestroySingletonCollection;
extern NSString *const willDestroySingletonCollectionObject;
RMSingletonCollector.m
//
// RMSingletonCollector.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/11/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMSingletonCollector.h"
#import <objc/objc-runtime.h>
NSString *const willDestroySingletonCollection = #"willDestroySingletonCollection";
NSString *const willDestroySingletonCollectionObject = #"willDestroySingletonCollectionObject";
RMInitializerData RMInitializerDataMake(SEL initializer, id data) {
RMInitializerData newData;
newData.designatedInitializer = initializer;
newData.data = data;
return newData;
}
#implementation NSObject (SingletonCollector)
+ (RMInitializerData)designatedInitializerForIdentifier:(NSString *)identifier {
return RMInitializerDataMake(#selector(init), nil);
}
#end
#interface RMSingletonCollector ()
+ (NSMutableDictionary *)singletonCollection;
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection;
#end
#implementation RMSingletonCollector
static NSMutableDictionary *singletonCollection = nil;
+ (NSMutableDictionary *)singletonCollection {
if (singletonCollection != nil) {
return singletonCollection;
}
NSMutableDictionary *collection = [[NSMutableDictionary alloc] initWithCapacity:1];
[self setSingletonCollection:collection];
[collection release];
return singletonCollection;
}
+ (void)setSingletonCollection:(NSMutableDictionary *)newSingletonCollection {
if (newSingletonCollection != singletonCollection) {
[singletonCollection release];
singletonCollection = [newSingletonCollection retain];
}
}
+ (id)collectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
id obj;
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
if (obj = [[self singletonCollection] objectForKey:key]) {
return obj;
}
// dynamic creation.
// get a class for
Class classForName = NSClassFromString(className);
if (classForName) {
obj = objc_msgSend(classForName, #selector(alloc));
// if the initializer takes an argument...
RMInitializerData initializerData = [classForName designatedInitializerForIdentifier:identifier];
if (initializerData.data) {
// pass it.
obj = objc_msgSend(obj, initializerData.designatedInitializer, initializerData.data);
}
else {
obj = objc_msgSend(obj, initializerData.designatedInitializer);
}
[singletonCollection setObject:obj forKey:key];
[obj release];
}
else {
// raise an exception if there is no class for the specified name.
NSException *exception = [NSException exceptionWithName:#"com.RMDev.RMSingletonCollector.failed_to_find_class" reason:[NSString stringWithFormat:#"SingletonCollector couldn't find class for name: %#", [className description]] userInfo:nil];
[exception raise];
[exception release];
}
return obj;
}
+ (id<RMWeakObjectReference>)referenceForObjectOfType:(NSString *)className identifier:(NSString *)identifier {
id obj = [self collectionObjectForType:className identifier:identifier];
RMWeakObjectRef *objectRef = [[RMWeakObjectRef alloc] initWithObject:obj identifier:identifier];
return [objectRef autorelease];
}
+ (void)destroyCollection {
NSDictionary *userInfo = [singletonCollection copy];
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollection object:self userInfo:userInfo];
[userInfo release];
// release the collection and set it to nil.
[self setSingletonCollection:nil];
}
+ (void)destroyCollectionObjectForType:(NSString *)className identifier:(NSString *)identifier {
NSString *key;
if (identifier) {
key = [className stringByAppendingFormat:#".%#", identifier];
}
else {
key = className;
}
[[NSNotificationCenter defaultCenter] postNotificationName:willDestroySingletonCollectionObject object:[singletonCollection objectForKey:key] userInfo:nil];
[singletonCollection removeObjectForKey:key];
}
#end
RMWeakObjectRef.h
//
// RMWeakObjectRef.h
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
// In order to offset the performance loss from always having to search the dictionary, I made a retainable, weak object reference class.
#import <Foundation/Foundation.h>
#protocol RMWeakObjectReference <NSObject>
#property (nonatomic, assign, readonly) id objectRef;
#property (nonatomic, retain, readonly) NSString *className;
#property (nonatomic, retain, readonly) NSString *objectIdentifier;
#end
#interface RMWeakObjectRef : NSObject <RMWeakObjectReference>
{
id objectRef;
NSString *className;
NSString *objectIdentifier;
}
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier;
- (void)objectWillBeDestroyed:(NSNotification *)notification;
#end
RMWeakObjectRef.m
//
// RMWeakObjectRef.m
// RMSingletonCollector
//
// Created by Rich Meade-Miller on 2/12/11.
// Copyright 2011 Rich Meade-Miller. All rights reserved.
//
#import "RMWeakObjectRef.h"
#import "RMSingletonCollector.h"
#implementation RMWeakObjectRef
#dynamic objectRef;
#synthesize className, objectIdentifier;
- (RMWeakObjectRef *)initWithObject:(id)object identifier:(NSString *)identifier {
if (self = [super init]) {
NSString *classNameForObject = NSStringFromClass([object class]);
className = classNameForObject;
objectIdentifier = identifier;
objectRef = object;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollectionObject object:object];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(objectWillBeDestroyed:) name:willDestroySingletonCollection object:[RMSingletonCollector class]];
}
return self;
}
- (id)objectRef {
if (objectRef) {
return objectRef;
}
objectRef = [RMSingletonCollector collectionObjectForType:className identifier:objectIdentifier];
return objectRef;
}
- (void)objectWillBeDestroyed:(NSNotification *)notification {
objectRef = nil;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[className release];
[super dealloc];
}
#end
The easiest (and thread safe) acces to singleton:
static SomeClass* shared_instance;
+(SomeClass*) sharedInstance {
#synchronized( shared_instance ) {
if( !shared_instance ) {
shared_instance = [[SomeClass alloc] init];
//some additional initialization here
}
}
return shared_instance;
}
Edit:
I think particular singleton class would be your best choice. Even for lazy loading. E.g. you need to access some array of users all over your app. Then you can create singleton class:
UserManager:
+(UserManager*) sharedManager;
-(NSArray*) allUsers;
-(NSArray*) recentUsers;
-(NSArray*) featuredUsers;
-(void) addUser:(User*) user;
-(void) removeUser:(User*) user;
etc...
Then you will be able to access that arrays in every view controller.
You should create singleton for other types.
Hi have a problem with singleton Class in iPhone app.
I have create a simple class for visualization of NSString value.
My problem is generate when i try to stamp a NSString in a textVIew.
I call my methods and the value of string in Singleton class is (invalid) (i have tested it with debug).
Can you help me with the code solution.
my code:
#import "UntitledViewController.h"
#import "SingletonController.h"
#implementation UntitledViewController
#synthesize resetTextEvent;
#synthesize buttonSavePosition;
-(IBAction)stamp{
textEvent.text = [sharedController name];
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedController = [SingletonController sharedSingletonController];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
#import "SingletonController.h"
#implementation SingletonController
#synthesize name;
static SingletonController *sharedController = nil;
+(SingletonController*)sharedSingletonController{
if(sharedController == nil){
sharedController = [[super allocWithZone:NULL] init];
}
return sharedController;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedSingletonController] 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;
}
-(id)init{
self = [super init];
if (self != nil) {
name = [NSString stringWithFormat:#"hello"];
}
return self;
}
-(void) dealloc {
[super dealloc];
}
#end
This line:
name = [NSString stringWithFormat:#"hello"];
is problematic. name refers to an instance variable, not your property. So what's happening is your string is being assigned to name, but it's an autoreleased object. So, at some point in the future, name is released automatically and refers to deallocated memory.
If you've specified the name property as either retain or copy, then either of the following lines will property retain the object:
self.name = [NSString stringWithFormat:#"hello"];
name = [[NSString stringWithFormat:#"hello"] retain];