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]
-------------------------
Related
This question already has answers here:
How to save NSMutablearray in NSUserDefaults
(9 answers)
Closed 5 years ago.
In one view controller, I store some array value and I will get that value from another view controller.
How to save array value in shared manager and how to retrieve that array value from another view controller? How to use shared preference in Xcode? Is that possible? Please help me with the example.Thank you.
my code is
(id)sharedManager {
static AssistantView *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
and i put the value
[[AssistantView sharedManager]results]; // where results is array
Use NSUserDefaults
Set
[[NSUserDefaults standardUserDefaults] setObject:#"Your Object" forKey:#"NameOfPreference"];
[[NSUserDefaults standardUserDefaults] synchronize];
Get
NSString *savedObject = [[NSUserDefaults standardUserDefaults]
stringForKey:#"NameOfPreference"];
// its Your Object
Alternative using AppDelegate
1 Define Macro
#define APPDELEGATE ((AppDelegate *)[[UIApplication sharedApplication] delegate])
2 add property for object in Appdelegate
#property (nonatomic) NSArray *arrObjects;
3 set object to property .
APPDELEGATE.arrObjects = #[#"",#""];
4 Get value
NSArray *globalObjects = APPDELEGATE.arrObjects
you should use a singleton class like this
#import <foundation/Foundation.h>
#interface MySingleton : NSObject {
}
#property (nonatomic, retain) NSArray *mySharedArray;
+(id)sharedMySingleton;
#end
and in .m file
#implementation MySingleton
static MySingleton* _sharedMySingleton = nil;
+(id)sharedMySingleton
{
#synchronized([MySingleton class])
{
if (!_sharedMySingleton)
_sharedMySingleton = [[self alloc] init];
return _sharedMySingleton;
}
return nil;
}
and call it in any viewController like
[[MySingleton sharedMySingleton] mySharedArray];
1) Create a File with NSObject as subclass (i.e SharedClass)
SharedClass.m
#implementation SharedClass
+ (instancetype)sharedManager {
static SharedClass *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
- (instancetype)init {
if (self = [super init]) {
// alloc or set object here
return self;
}
SharedClass.h
#import <Foundation/Foundation.h>
#interface SharedClass : NSObject
+ (instancetype)sharedManager;
// your array
#end
2) To access array
[[SharedClass sharedManager]array];
3) To set array
[[SharedClass sharedManager]setArray];
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 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;
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.
The same code works on iOS 5 but not in iOS 6. That is the count shows as being 0. Any ideas?
It should say atleast 1 as the count since I have verified house.png to be a valid image.
Here is my code:
MyManager * myManager = [MyManager sharedInstance];
NSString *pathOfImageFile = [[NSBundle mainBundle] pathForResource:#"house" ofType:#"png"];
UIImage *myImage = [UIImage imageWithContentsOfFile:pathOfImageFile];
UIImageView * tempImageView = [[UIImageView alloc] initWithImage:myImage];
[myManager.assets addObject:tempImageView];
NSLog(#"image count: %d", [myManager.assets count]);
Here is my singleton:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyManager : NSObject
{
MyManager *_sharedObject;
NSMutableArray * assets;
}
//Property Listing
#property(nonatomic,copy) NSString * postTitle;
#property(nonatomic,copy) NSString * postText;
#property(nonatomic,copy) NSString * postLink;
#property(nonatomic,copy) NSString * postCategory;
//assets
#property (nonatomic, strong) NSMutableArray * assets;
+ (id)sharedInstance;
- (void)reset;
#end
#import "MyManager.h"
#implementation MyManager
//Property Listing
#synthesize postTitle=_postTitle;
#synthesize postText=_postText;
#synthesize postLink=_postLink;
#synthesize postCategory=_postCategory;
#synthesize assets=_assets;
- (id)init
{
self = [super init];
if ( self )
{
assets = [[NSMutableArray alloc] init];
NSLog(#"Singleton Initialized...");
}
return self;
}
+ (id)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init]; // or some other init method
});
return _sharedObject;
}
- (void)reset
{
self.postTitle =#"";
self.postText=#"";
self.postLink=#"";
self.postCategory=#"";
[self.assets removeAllObjects];
}
#end
You have extra ivars related to your assets property.
You define a property named assets. You then (needlessly) synthesize the property specifying that the generated ivar should be named _assets.
You also (needlessly) declare an explicit ivar named assets.
In your init method you assign the array to the assets ivar. In your reset method you clear the assets property (using the _assets ivar).
Get rid of the explicit assets ivar. Get rid of the #synthesize statement. This will leave you with an auto generated ivar of _assets.
Update your code to use either the property or the _assets ivar.
Try:
_assets = [[NSMutableArray alloc] init];
And you are defining sharedObject in your interface as a property: MyManager *_sharedObject;
You don't want that if you'll always grab your instance via [MyManager sharedInstance]; which is holding the initialized instance in that local static var.