I have a complete noob question for you. I'm obviously rusty with obj-c. I have a simple shopping cart class implemented as a singleton and just want it to store a single NSMutableDictionary. I want to be able to add objects to this dictionary from anywhere in the app. But for some (I'm sure simple) reason it's just returning null. No error messages.
ShoppingCart.h:
#import <Foundation/Foundation.h>
#interface ShoppingCart : NSObject
// This is the only thing I'm storing here.
#property (nonatomic, strong) NSMutableDictionary *items;
+ (ShoppingCart *)sharedInstance;
#end
ShoppingCart.m:
// Typical singelton.
#import "ShoppingCart.h"
#implementation ShoppingCart
static ShoppingCart *sharedInstance = nil;
+ (ShoppingCart *)sharedInstance
{
#synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[self alloc] init];
}
return(sharedInstance);
}
#end
And in my VC I'm trying to set it with:
- (IBAction)addToCartButton:(id)sender
{
NSDictionary *thisItem = [[NSDictionary alloc] initWithObjects:#[#"test", #"100101", #"This is a test products description"] forKeys:#[#"name", #"sku", #"desc"]];
// This is what's failing.
[[ShoppingCart sharedInstance].items setObject:thisItem forKey:#"test"];
// But this works.
[ShoppingCart sharedInstance].items = (NSMutableDictionary *)thisItem;
// This logs null. Specifically "(null) has been added to the cart"
DDLogCInfo(#"%# has been added to the cart", [[ShoppingCart sharedInstance] items]);
}
Thanks
You are never creating a NSMutableDictionary object named items.
You could create it in the init of ShoppingCart.
-(id)init
{
if(self = [super init]) {
_items = [NSMutableDictionary dictionary];
}
return self;
}
or in sharedInstance
+ (ShoppingCart *)sharedInstance
{
#synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[self alloc] init];
sharedInstance.items = [NSMutableDictionary dictionary];
}
return(sharedInstance);
}
I might also add it's better (arguably) to set up your shared instance like so:
static ShoppingCart *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
instance.items = [NSMutableDictionary dictionary];
});
return instance;
Related
I've been trying to implement a global NSMutableArray from what I think to be a singleton class that I've implemented.
I can enter ViewController # 2, add and remove objects to the array.
However, when I leave ViewController #2 and come back, the data does not persist, and I have an array with 0 objects.
What do you think I'm doing wrong?
.h
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
.m
#import "GlobalArray.h"
#implementation GlobalArray
static GlobalArray* sharedGlobalArray;
NSMutableArray* globalArray;
+(void)initialize{
static BOOL initalized = NO;
if(!initalized){
initalized = YES;
sharedGlobalArray = [[GlobalArray alloc] init];
}
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
View Controller #2
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
//Various add and remove code
Thank you for your input.
Following is best approach to share data Globally at Application level. Singleton Class is a key. Singleton is only initialised once, rest of times shared data is returned.
#interface Singleton : NSObject
#property (nonatomic, retain) NSMutableArray * globalArray;
+(Singleton*)singleton;
#end
#implementation Singleton
#synthesize globalArray;
+(Singleton *)singleton {
static dispatch_once_t pred;
static Singleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
shared.globalArray = [[NSMutableArray alloc]init];
});
return shared;
}
#end
Following is the way to access/use shared data.
NSMutableArray * sharedData = [Singleton singleton].globalArray;
You create separate instance of GlobalArray in your ViewController#2 with this code:
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
Instead, you should create accessor method to return your shared instance, something like this:
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
+(GlobalArray*)sharedInstance;
with implementation:
// GlobalArray.m
// ... your existing code
// accessor method
+(GlobalArray*)sharedInstance
{
return sharedGlobalArray;
}
and then call it from your ViewController#2:
GlobalArray* myGlobalArray = [GlobalArray sharedInstance];
However, using global variables to transfer data between view controllers is bad practice; I suggest you to use more safe methods, create a delegate, for example.
To create a shared global array, if that's really what you want, just put this in the header file:
extern NSMutableArray *myGlobalArray;
and this in your main source file:
NSMutableArray *myGlobalArray;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
myGlobalArray = [NSMutableArray new];
}
Use this code for set and get the array views, for adding and removing do it separate in controller itself.
// GlobalArray.h
#interface GlobalArray : NSObject
#property (nonatomic, strong) NSMutableArray* globalArray;
+ (id)sharedManager;
-(NSMutableArray *) getGlobalArray;
-(void) setGlobalArray:(NSMutableArray *)array;
#end
/*-----------------------------------------*/
#import "GlobalArray.h"
#implementation GlobalArray
+ (id)sharedManager {
static GlobalArray *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
-(NSMutableArray *) getGlobalArray{
return self.globalArray;
}
-(void) setGlobalArray:(NSMutableArray *)array{
_globalArray = globalArray;
}
#end
-------------------------
//get array
NSArray * array = [[GlobalArray sharedManager] getGlobalArray];
//set array
[[GlobalArray sharedManager] setGlobalArray:array]
-------------------------
I made a singleton and i'm trying to add objects to self.timelineArray but i can't. When i do this i have 9 objects:
NSNumber* nmb = [[NSNumber alloc] initWithInt:1];
[self.dataManager.timelineArray addObject:nmb];
After i insert the nmb object, i still have 9 objects.
Here's my singleton header (only important bits):
#interface DataManager : NSObject
#property (nonatomic, strong) NSMutableArray* timelineArray;
Here's my singleton implementation (only important bits):
#import "DataManager.h"
static DataManager* sharedInstance = nil;
#implementation DataManager
+ (DataManager *) sharedInstance{
#synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
- (id)init {
if (self = [super init])
{
self.timelineArray = [[NSMutableArray alloc] init];
}
return self;
}
This is just simple coding and i can't seem to figure out why it does not work.
Trying to modify immutable array(NSArray) will lead to this.
Replace
[responseObject objectForKey:#"timeline"] with [[NSMutableArray alloc] initWithArray:[responseObject objectForKey:#"timeline"]
This was based on the comments of the question.
i'll suggest you to add like following
NSNumber* nmb = [[NSNumber alloc] initWithInt:1];
[[DataManager sharedInstance]timelineArray addObject:nmb];
this will always add into the singleton not the instance of singleton
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 an NSMutableArray in a singleton, which I want to access in two classes. I've done this in another project exactly like this (that one had ARC, this one doesn't) and it worked there.
Project doesn't have ARC on.
I'm getting the error:
*** -[CFString isNSString__]: message sent to deallocated instance 0xb3d1db0
StoreVars.h
#interface StoreVars : NSObject
#property (nonatomic, retain) NSMutableArray *sharedArrayOfVideo;
+ (StoreVars *)sharedInstance;
#end
StoreVars.m
#implementation StoreVars
#synthesize sharedArrayOfVideo;
+ (StoreVars *) sharedInstance {
static StoreVars *myInstance = nil;
if (myInstance == nil) {
myInstance = [[[[self class] alloc] init] retain];
myInstance.sharedArrayOfVideo = [[NSMutableArray alloc] init];
}
return myInstance;
}
#end
Populating the array asynchronously:
NSDictionary *tempDict = [[NSDictionary alloc] initWithObjectsAndKeys:
ID,#"id",
title,#"title", nil];
[[StoreVars sharedInstance].sharedArrayOfVideo addObject:tempDict];
This is where crash happens:
NSLog(#"%i",[[StoreVars sharedInstance].sharedArrayOfVideo count]);
NSLog(#"%#",[StoreVars sharedInstance]);
if ([[StoreVars sharedInstance] respondsToSelector:#selector(sharedArrayOfVideo)]) {
**NSLog(#"%#",[StoreVars sharedInstance].sharedArrayOfVideo);**
//NSLog(#"%#",[[StoreVars sharedInstance].sharedArrayOfVideo objectAtIndex:8]);
}
Output:
10
<StoreVars: 0xb381b00>
*** -[CFString isNSString__]: message sent to deallocated instance 0xb3d1db0
Found the problem, when creating the dictionary, I did:
NSString *ID = [[[arrayOfEntry objectAtIndex:index] objectForKey:#"id"]; autorelease]
NSString *title = [[[arrayOfEntry objectAtIndex:index] objectForKey:#"title"] autorelease];
Instead of:
NSString *ID = [[arrayOfEntry objectAtIndex:index] objectForKey:#"id"];
NSString *title = [[arrayOfEntry objectAtIndex:index] objectForKey:#"title"];
NSDictionary *tempDict = [NSDictionary dictionaryWithObjectsAndKeys:ID,#"id",title,#"title", nil];
Try to create singleton with gcd like this:
static ASBTLEManager *sharedInstance = nil;
+ (ASBTLEManager *)sharedManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ASBTLEManager alloc] initInstance];
});
return sharedInstance;
}
try this way to create singleton instance, this is more accurate than what you are trying
+(StoreVars *) sharedInstance
{
static StoreVars * myInstance = nil;
static dispatch_once_t oneTimePredicate;
//get calls only once.
dispatch_once(&oneTimePredicate, ^{
myInstance = [[self alloc] init];
myInstance.sharedArrayOfVideo = [[NSMutableArray alloc] init];
});
return myInstance;
}
I have a question with regards to the line below in your code:
if ([[StoreVars sharedInstance] respondsToSelector:#selector(sharedArrayOfVideo)])
Q. Why have you tried to check the mutableArray as method via selector ?
If you want to iterate through the added dictionaries do the following
StoreVars *storeVars = [StoreVars sharedInstance];
if(storeVars.sharedArrayOfVideo.count>0)
{
for(int i=0; i<storeVars.sharedArrayOfVideo.count; i++)
{
NSDictionary *dictionary = [storeVars.sharedArrayOfVideo objectAtIndex:i];
// do stuff with the grabbed dictionary.
}
}
else { NSLog(#"sharedArrayOfVideo is empty"); }
Further in your singleton creation code perform the following edit:
Instead of following:
if (myInstance == nil) {
myInstance = [[[[self class] alloc] init] retain];
Use the following:
if (myInstance == nil) {
myInstance = [[StoreVars alloc] init];
// you already own the above object via alloc/init, so no extra retains.
I am currently writing a helper class for my app. The helper class will return globally accessible variables. I created a simple helper as shown below:
#interface MyHelper : NSObject
{
}
+(id) sharedHelper;
+(NSMutableArray *) employers;
+(id) sharedHelper
{
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
+(NSMutableArray *) employers
{
return _employers;
}
Now I can access the employers like this:
[MyHelper employers] and I can also access it like this [[MyHelper sharedHelper] employers] What is the benefit for each approach or they they both same.
I suppose that employers array is a property of your class MyHelper.
If you call [MyHelper employers] without call [MyHelper sharedHelper] you can get incorrect result (the value of employers array is garbage).
Maybe the best practice here is to use lazy loading in +(NSMutableArray *) employers and get static variable out of +(id) sharedHelper:
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
+(id) sharedHelper
{
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
+(NSMutableArray *) employers
{
if(!sharedHelper)
[MyHelper sharedHelper];
return _employers;
}
You employers class method is referencing _employers, which is presumably an instance variable for your class. You cannot do that. Furthermore, even if you did some sloppy workaround, like a global variable, you have no assurances that _employers has been instantiated unless the employers class method also ensures that the sharedHelper is called.
So, a couple of thoughts:
You really should have some class property for employers (I assume you did, but omitted it for brevity, but let's include it here to eliminate ambiguity):
#interface MyHelper : NSObject
#property (nonatomic, strong) NSMutableArray *employers;
#end
I presume your init method would initialize this employers object
- (id)init
{
self = [super init];
if (self) {
_employers = [[NSMutableArray alloc] init];
}
return self;
}
Your existing sharedHelper method is perfectly fine.
If you're going to have an employers class method, though, should access the employers instance method:
+ (NSMutableArray *)employers
{
return [[MyHelper sharedHelper] employers];
}
Having done all of that, your class would look like:
#interface MyHelper : NSObject
#property (nonatomic, strong) NSMutableArray *employers;
+ (id)sharedHelper;
+ (NSMutableArray *)employers;
#end
#implementation MyHelper
+ (id)sharedHelper
{
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
- (id)init
{
self = [super init];
if (self) {
_employers = [[NSMutableArray alloc] init];
}
return self;
}
+ (NSMutableArray *)employers
{
return [[MyHelper sharedHelper] employers];
}
#end
That works, but having said that, as a matter of style, I personally wouldn't recommend a class method, employers, when you have a getter method for a property of the same name. Seems a little confusing. I would excise that employers class method, and stick with the standard getter method that will be synthesized for you, resulting in just:
#interface MyHelper : NSObject
#property (nonatomic, strong) NSMutableArray *employers;
+ (id)sharedHelper;
#end
#implementation MyHelper
+ (id)sharedHelper
{
static MyHelper *sharedHelper = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
sharedHelper = [[self alloc] init];
});
return sharedHelper;
}
- (id)init
{
self = [super init];
if (self) {
_employers = [[NSMutableArray alloc] init];
}
return self;
}
#end
And then other code using this MyHelper class can simply refer to [[MyHelper sharedHelper] employers] themselves.