One NSDictionary visible everywhere in application - objective-c

Now I am developing an iOS application which works like this:
User scans QR code,
App searches for a specific key - > value,
it gives out a value to the user.
Currently I have two ViewControllers - the main and "value" ViewController, which is inherited from main. The problem is that if I create NSDictionary in main VC it is not visible in "value" VC. Main VC gives only the string (QR code, the key) through the segue. So, the value VC has to search for key and display the value.
What I ask is some kind of global variable or one DataSource visible across the whole app. Of course, I can implement NSDictionary initialisation inside value ViewDidLoad method and it will work, but this is not the point. New modules are to be added there and the variable has to be global. I googled a lot and got the idea that singleton pattern can be helpful here. I tried to implement it, but no idea how to do. Do I need it, or it is too complex for this kind of DataSource?
Thank you!

The basic idea is, you will still need to #include the header file of the place where this dictionary will be. The solution that Naveen proposes means that you will be including the header for the app delegate wherever you want to access it. Whether to use the app delegate for this purpose or not is kinda grayish. Some people often do this, some say its a bad use of it.
The singleton approach means that you will create a class, that will always contain the same information since the init method will return object that was previously created.
For the singleton aproach, imagine I have a database manager class. So in the header of this class (the DatabaseManagerSingleton.h) ill have this:
#interface DatabaseManager : NSObject
+ (DatabaseManager*)sharedInstance;
// Your dictionary
#property (nonatomic,strong) NSMutableDictionary* someDictionary;
The implementation will look like this: (check how "sharedInstance" initializes the object)
#implementation DatabaseManager
#pragma mark - Singleton Methods
+ (DatabaseManager*)sharedInstance {
static DatabaseManager *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// Custom initialization
_someDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
Now, a VERY important thing is that, any place you want to use this object should first include the header:
EDIT: To use it in your code:
1) add the header
#import "DatabaseManager.h"
2) initialize the object
DatabaseManager *databaseManager = [DatabaseManager sharedInstance];
3) do whatever you need
// Initialize the dictionary
databaseManager.someDictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"OBJECT",#"someKey", nil]; // In this case the object is just a NSString.
// Access
[databaseManager.someDictionary objectForKey:#"someKey"];

Put as a property on Appdelegate
#property (nonatomic,strong) NSDictionary * sharedData;
Access anywhere like
NSDictionary *sharedData= ((APPDelegate *) [UIApplication sharedApplication].delegate).sharedData;

Related

Objects In Objective-C Singleton becomes null

First of all I want to say that I am very new at both Objective-C and posting here on stackoverflow.
I am using a singleton in a tab bar application which also reads data from an API located on a web server.
My problem is, that when I first instantiate my singleton it works, but as soon as I change view by clicking in the tab bar something weird is happening. My User object which I have in my singleton is still there, but all its properties are set to null.
My singleton is implemented in the following way:
#implementation cacheobject
#synthesize location;
#synthesize currentUser;
#synthesize login;
#synthesize email2;
#synthesize isExistingUser;
+ (id)sharedCacheobject {
static cacheobject *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
self.location = 1;
}
return self;
}
- (void)dealloc {
NSLog(#"DEALLOC!!!!!!!!");
}
#end
I am using the singleton in the following way:
cacheobject *co = [cacheobject sharedCacheobject];
NSString *first = co.currentUser.firstname;
firstname2.text = first;
Whatever view is the first view in my tab bar the singleton works correctly, but as soon as I navigate to another view, my properties are null. Everything is null except for the location property.
I have logged the user object, and in both views the object comes out as:
<User: 0x8da1640>
And since the hex value is the same my guess is, that it is the same object, but the properties are just null??
Please help, I have spent two days trying to figure this out.
Kind regards.
I found the answer.
As KerrM suggested the properties in my user class was not declared correctly.
They should be strong and retain but they were weak and nonatomic.
Thanks for the help.
You're resetting the sharedCacheobject each time you call [cacheobject sharedCacheobject] but you're only initializing it once. So, the first time you're first setting the object to nil and then initializing it in your dispatch_once block. Every time after that you're setting the sharedCacheobject to nil but since the onceToken has been used it's never getting reinitialized.
If you call it twice in the same view it should still be returning nil, it just so happens that the second time you call sharedCacheobject is in another view.
Try changing
static cacheobject *sharedMyManager = nil;
to
static cacheobject *sharedMyManager;

Objective-c: Singleton - passing variables

I have a singleton that I'd like to use to manage the onscreen animation of my views. Here's my.
#import <Foundation/Foundation.h>
#interface OAI_AnimationManager : NSObject {
NSMutableDictionary* sectionData;
}
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
.m file
#import "OAI_AnimationManager.h"
#implementation OAI_AnimationManager
#synthesize sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
static OAI_AnimationManager* sharedAnimationManager;
#synchronized(self) {
if (!sharedAnimationManager)
sharedAnimationManager = [[OAI_AnimationManager alloc] init];
return sharedAnimationManager;
}
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", sectionData);
}
#end
You'll see in the .h file I added a NSMutableDictionary and am using #property/#synthesize for it's getter and setter.
In my ViewController I instantiate the animation manager as well as a series of subclasses of UIView called Section. With each one I store the data (x/y w/h, title, etc.) in a dictionary and pass that to the dictionary delcared in animation manager. In the Section class I also instantiate animation manager and add a UITapGestureRecognizer which calls a method, which passes along which section was tapped to a method (checkToggleStatus) in animation manager.
As you can I see in the method I am just logging sectionData. Problem is I am getting null for the value.
Maybe my understanding of singletons is wrong. My assumption was the class would only be instantiated once, if it was already instantiated then that existing object would be returned.
I do need all the other Section classes data as if one animates others animate in response and I can get around it by passing the tapped Section to the animation manager and doing [[Section superview] subviews] and then looping and getting the data from each that way but it seems redundant since that data is available in the ViewController when they are created.
Am I doing something wrong in trying to transfer that data? Is there a better solution? I am open to suggestions and criticisms.
Thanks
h file
#interface OAI_AnimationManager : NSObject
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
m file
static OAI_AnimationManager* _sharedAnimationManager;
#implementation OAI_AnimationManager
#synthesize sectionData = _sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
#synchronized(self) {
if (!_sharedAnimationManager) {
_sharedAnimationManager = [[OAI_AnimationManager alloc] init];
}
}
return _sharedAnimationManager;
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", _sectionData);
}
#end
Notice I moved your sectionData variable from the header and moved it to the implementation file. A while back, they changed it to where you can synthesize properties and specify their instance variable names along side it... hence:
sectionData = _sectionData;
I also added and underscore to the instance variable... this is a universal convention for private variables and it also will throw a compile error now if you try to type just sectionData as you did in the return statement of checkToggleStatus:. Now you either have to type self.sectionData or _sectionData.
You didn't include the code that creates an instance of your dictionary but I bet you didn't set it as self.sectionData = [[NSDictionary alloc] init] which means it would not retain the value and you would get null the next time you called it. Classic memory management mistake... I know it well because I learned the hard way hehehe

Structure (struct) in Xcode as Array, and global

I'm trying to create a structure, which should be used as an array.
Then at the same time I need to access it from a different view, so I'm guessing I need to make it global for now, until I learn a better way (have tried many suggestions from google searches)
But how do I declare a variable twice?
I mean both as my structure, and as an array?
Sometimes "global variables" can be appropriate, but not simply as "global variables". The proper way to do it in Objective C is with singletons.
Here is part of the .h implementation below:
#interface MySingleton : NSObject
#property (nonatomic, strong) NSMutableArray *myArray;
+ (MySingleton *)sharedSingleton
#end
And here is what the .m implementation would look like:
#implementation MySingletion
#synthesize myArray;
static MySingleton *shared = nil;
- (id)init
{
self = [super init];
if(self)
{
myArray = [[NSMutableArray alloc] init];
}
return self;
}
+ (MySingleton *)sharedSingleton
{
if(shared == nil)
{
shared = [[MySingleton alloc] init];
}
return shared;
}
Then, whenever you wanted to access this from a given class, you would #import "MySingleton.h" and access your array like so:
[MySingleton sharedSingleton].myArray
Edit: However, this does not mean that every time you need to pass data it should be done with a singleton. Singletons are correct in certain situations, but as others have mentioned, you should pass the data to the next class yourself in most situations.
Well i just created a new h and m fil, where i put a lot of varibales
using extend in the.h file
and alloc init in the .m file
Then i just include this file where i need the variables. Probably there is a much better way, but this was the only one i could figure out.

Singleton - Reading from a Data Store

I'm trying to do the right thing by not using global variables in my Xcode project. I've successfully created a singleton. I have a simple data store which I named "myContactsStore" that contains an array, with each object in the array having only 3 instance variables (name, phoneNum, and eMail). I have no problem creating, modifying, saving etc. the data in the array when I'm executing the view controller that created the array.
My problem is trying to access the data store from another view controller. I'm halfway there, as proven by my ability to print the contents of the entire test array from another view controller by using the following code in a for loop:
NSLog(#"%#", myContactsStore.description);
Here's the output:
"Mary, 0938420839, PaulDoe#Mac.com",
"John, 9932097372, PaulDoe#Mac.com",
"Mary, 0726756893, RedCat#iwon.com",
"Mary, 8556327199, xxxbct#mac.com",
"John, 0640848317, xxxbct#mac.com"
How do I access just one instance variables? For example, I want to create a read-only array in another view controller that contains just the email addresses of every contact in the "myContactsStore" array. I've tried several things, but I'm new at this and I must be missing something very basic.
Thanks for you help and any code example you might have the time to include.
While Singletons are an easy way to share data across classes they cause cohesion problems in your overall design where your classes start to "import the world". The issue is coming up with exactly the right dependency for a given class and this is very easily missed when you try to design from classes instead of designing from use cases. You want to ask yourself, "what data does this class use?" You then create a protocol (abstraction) that gives the class the view of the data it wants. In your case one class writes to the data store. It doesn't need to create the data store nor does either class need to know that the data is maintained in an array. Follow these steps exactly in order and follow directly or you'll miss my point. Try a protocol in a separate .h file like the following:
#protocol MyDataStore <NSObject> {
}
Import this header in your 1st class that creates the contacts and declare a property of the protocol's type.
#import "MyDataStore.h"
#interface MyContactCreator : UIViewController
#property (nonatomic, retain) id<MyDataStore> dataStore;
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore;
#end
I threw in a custom init method in case you are currently instantiating your view controllers programmatically instead of via InterfaceBuilder. In your implementation you would do something like this:
#implementation MyContactCreator
//other methods...
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil dataStore:(id<MyDataStore>)aDataStore
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.dataStore = aDataStore;
}
return self;
}
//other methods...
#end
If you are using Interface Builder to create your view controller you can drag a custom object into play and call it something like "MyDataStoreImpl". The idea here is that you are giving the datastore to the view controller instead of it creating it directly and knowing about it. Also you want to defer worrying about how the data store works until you really need to. Later in your view controller where you create the contacts you would use the data store to create them. Assuming the info comes from standard screen elements you would write code like this:
-(void) addContactTapped:(id)sender
{
[self.datastore createContactWithName:txtNameField.text phoneNumber:txtPhoneField.text email:txtEmailField.text];
}
Your editor would scream at you (with little red marks like what your 2nd grade teacher would use on your spelling homework) because the datastore doesn't respond to the message you are sending. You go back and add that method to the protocol:
#protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
}
In your other view controller class that wants the list of email addresses you would import the same data store protocol. You would also declare a datasource property identical as what we did above using a complimentary custom init method or Interface Builder to pass the datasource in. This view controller (assuming it's a table view controller) would probably have some methods like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.datasource numberOfContacts];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *email = [self.datasource emailForContactNumber:indexPath.row];
UITableViewCell *cell = //create tableview cell with the email string
return cell;
}
Your editor will start screaming with the little red lines and all. This is where you go and add more methods to the protocol.
#protocol MyDataStore <NSObject> {
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
-(NSInteger) numberOfContacts;
-(NSString*) emailForContactNumber:(NSInteger)index;
}
The little red lines go away and finally you can begin thinking about how the contacts are stored and retrieved. Create a separate class called MyDataStoreImpl which extends NSObject and imports and follows the "MyDataStore" protocol. Fill out implementations of all of the methods and you should be up and running. It could be as simple as storing NSDisctionary objects containing the contact info in an internal NSMutableArray property.
- (id)init {
self = [super init];
if (self) {
self.allContacts = [NSMutableArray array];
}
return self;
}
-(void) createContactWithName:(NSString*)aName phoneNumber:(NSString*)aPhoneNumber email:(NSString*)anEmail;
{
NSDictionary *newContact = [NSDictionary dictionaryWithObjectsAndKeys:
aName, #"name", aPhoneNumber, #"phone", anEmail, #"email",
nil];
[self.allConstacts addObject:newContact];
}
-(NSInteger) numberOfContacts;
{
return [self.allContacts count];
}
-(NSString*) emailForContactNumber:(NSInteger)index;
{
[[self.allContacts objectAtIndex:index] valueForKey:#"email"];
}
The advantages here are many. You can later re-implement the datasource to read/write from a plist file, network server, or database without touching any of your controllers. Also, your app will be easier to optimize for performance because you can design the read write methods to pull directly from a source instead of naively copying data from one array to another as you would if you were worrying about how it is managed too early. All of the above thrown together without testing and likely has errors but given to illustrate a point of how to properly share data between controllers without Singletons while maintaining a testable and easily maintainable codebase.
You can stick this to any class to make it a singleton:
+(MySingleton *)singleton {
static dispatch_once_t pred;
static MySingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
shared.someVar = someValue;
});
return shared;
}
-(void) dealloc {
abort();
[someVar release];
[super dealloc];
}
Better yet, you can add the class as an ivar of the application delegate, which is a singleton you already have. You can get a reference to it like this:
// AppDelegate or whatever name
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
Then you access the datastore as an ivar on that delegate, and optionally implement one of the several persistence technologies available on iOS, basically plist files through direct file access or NSCoding or even NSUserDefaults (which you shouldn't but it's handy for small tasks), Core Data, or SQLite.
If you are using this data store class only to pass data between controllers, you can do so directly instead. Example:
CitySelectionVC *citySel = [[CitySelectionVC alloc] initWithCities:self.cities];
[self.navigationController pushViewController:citySel animated:TRUE];
[citySel release];
I always use SynthesizeSingleton.h by Mike Gallagher. He has a really informative article relating to this topic here. You should really check it out. It makes the creation of Singleton classes really easy.
Assuming your myContactsStore object has a method contactsArray which returns the internal NSArray object, you can do this:
NSArray *emails = [myContactsStore.contentsArray valueForKey:#"email"];
NSLog(#"output: %#", emails);
Which should output:
output: (
"PaulDoe#Mac.com",
"PaulDoe#Mac.com",
"RedCat#iwon.com",
"xxxbct#mac.com",
"xxxbct#mac.com"
)
You simply have to enumerate through the array and retrieve the data you want. Like so:
NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:myContactsStore.count];
for (ContactClassName *contact in myContactsStore) {
NSString *email = contact.eMail; // I'm assuming your ivar eMail is also a property
if (email) [emails addObject:email];
}
You now have an NSMutableArray containing just the list of emails. If you want to make this list immutable simply do:
NSArray *emailList = [emails copy];

Singleton's property memory management

I'm trying to write my little app and experiencing some memory management problems.
At first, I have Game singleton object with property:
//Game.h
#interface Game : NSObject
#property (nonatomic, retain) MapBuildingsLayer *mapBuildingsLayer;
+(Game *) game;
-(BOOL) addObject:(NSString *) objName At:(CGPoint) pt;
#end
where MapBuildingsLayer is just cocos2d CCLayer instance
//Game.m
#implementation Game
#synthesize mapBuildingsLayer = _mapBuildingsLayer;
static Game *instance = nil;
+ (Game *)game {
#synchronized(self) {
if (instance == nil) {
instance = [[Game alloc] init];
}
}
return instance;
}
-(BOOL) addObject:(NSString *)objName At:(CGPoint)pt
{
if([objName isEqualToString:OBJ_TYPE_PIT])
{
if([[Game game].mapBuildingsLayer addPitAt:pt]) //app crashes here
{
[self toggleConstructionMode];
return YES;
}
}
return NO;
}
#end
In MapBuildingsLayer.m's init method I use Game's mapBuildingsLayer property to store a reference to this CCLayer instance in my singleton (for future use in other methods):
//MapBuildingsLayer.m
#implementation MapBuildingsLayer
-(id) init
{
if( (self=[super init])) {
[Game game].mapBuildingsLayer = self;
}
return self;
}
When I call Game's addObject:objName At: method, my app crashes with EXC_BAD_ACCESS.
How I must declare property in Game singleton to use it from other places in my app's lifetime?
You typically don't use the singleton in the class itself, try changing
if([[Game game].mapBuildingsLayer addPitAt:pt]) //app crashes here
to
if([self.mapBuildingsLayer addPitAt:pt]) //app crashes here
You should be using [Game game] external to the class to get into the singleton instance of your class and call its methods, but internal to the class you would just refer to it as self like normal.
Typically if you're going to use a game singleton this isn't how you'd use it. Try thinking of it like this with a giant state machine, Create a CCScene subclass which will initialize all of your respective CCLayer subclasses and control them. Then from the statemachine you can load the appropriate initializing its scene and that will create everything under it.
In your applicationDidFinishLaunching method you simply have your singleton object load your first scene. I'd really recommend checking out the Learning Cocos2d Book as it covers this singleton state engine very well and I think would clear up all your questions.
Bottom line is have the state engine load the scene which loads the layers.
No where in your code i am seeing your mapBuildingsLayer initialized. I hope before returning your instance you should also do
instance.mapBuildingsLayer = [CCLayer alloc] init];
I think the way you are assigning the mapBuildingsLayer is wrong. Remove [Game game].mapBuildingsLayer = self from your MapBuildingsLayer init method and instead add the following inside Game init method:
self.mapBuildingsLayer = [[MapBuildingsLayer alloc] init] autorelease];
now it is initialized inside your singleton init method so you can access it simply as [Game game].mapBuildingsLayer anywhere outside the Game class. If this doesnt work try posting what addPitAt: does.
hope this helps