Why is my NSMutableArray, located in a singleton, returning (null)? - objective-c

I am working in Xcode 4.5.1 in Objective-C. I’m making a hearing test and want to store relevant data to each question in an array. I made a singleton MyManager. I use this to store data.
It is working fine for simple int/float values etc., but I’m stuck trying to use NSMutableArray. I’m new to Objective-C, so I’m assuming/hoping I've made some obvious mistake.
So, I want to fill mySNRArray with float values. I’ve come to understand that I can’t simply add floats, because it will only take objects. Thus, I use NSNumber.
Problem: When I try to read the data that I’ve supposedly added to the NSMutableArray, I get (null).
I will now provide the relevant code:
MyManager.h
#interface MyManager : NSObject
{
NSMutableArray *mySNRArray;
}
#property (readwrite) NSMutableArray *mySNRArray;
+ (id)sharedManager;
#end
MyManager.m
#implementation MyManager
#synthesize mySNRArray;
+ (id)sharedManager
{
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init
{
if (self = [super init])
{
NSMutableArray *mySNRArray = [[NSMutableArray alloc]init];
}
return self;
}
#end
TestViewController.m
//First, I try to add a value to mySNRArray.
MyManager *Manager = [MyManager sharedManager];
NSNumber *tempStorage1 = [[NSNumber alloc] initWithFloat:mySNR];
[Manager.mySNRArray insertObject:tempStorage1 atIndex:questionNumber];
//The NSLog below is showing the correct value.
NSLog(#"%# : mySNR", tempStorage1);
...
for (n = 0; n < questionNumber; n++)
{
//Second, I try to acces the values I supposedly added to mySNRArray.
MyManager *Manager = [MyManager sharedManager];
//This NSLog below is showing (null).
NSLog(#"Value at %i in SNRArray = %#", n, [Manager.mySNRArray objectAtIndex:n]);
}
...
I hope somebody can explain my error.

change
NSMutableArray *mySNRArray = [[NSMutableArray alloc]init];
to
self->_mySNRArray = [[NSMutableArray alloc]init];
in your init method you are creating a local mutable array, but not assigning it to your property

Instead of creating a new object, use the ivar you created..in the init method.
_mySNRArray = [[NSMutableArray alloc]init];
Even you can ommit these, from your .h
{
NSMutableArray *mySNRArray;
}

+ (id)sharedManager
returns a value
static MyManager* sharedManager
Change the interface to
+ (MyManager*)sharedManager
and the compiler will tell you exactly what mistake you made.

Related

Singleton set in AppDelegate loses it value when allocated in another class

I have a iPad application where I'm attempting to use a singleton. This is the code in the .h file:
//-------------------------------------------
//-- singleton: timeFormat
#interface SingletonTimeFormat : NSObject {
}
#property (nonatomic, retain) NSNumber *timeFormat;
+ (id)sharedTimeFormat;
#end
This is the code from the .m file:
//-------------------------------------------
//-- SingletonTimeFormat
#implementation SingletonTimeFormat {
}
#synthesize timeFormat;
//-- sharedColorScheme --
+ (id)sharedTimeFormat {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-id) init {
self = [super init];
if (self) {
timeFormat = [[NSNumber alloc] init];
}
return self;
}
#end
I load the value (either 12 or 24) in AppDelegate - didFinishLaunchingWithOptions, then when I want to get the value of timeFormat I use this:
SingletonTimeFormat *stf = [[SingletonTimeFormat alloc]init];
if([stf.timeFormat isEqualToNumber: [NSNumber numberWithInt:12]]) {
which returns 0 (it was set correctly in AppDelegate, but apparently when I do the alloc in another class, it loses it's value. So obviously it's not working! (I have several other singletons that have the same pattern, but so far they appear to be working.
What's wrong here and how do I fix it?
You don't want to call your singleton using alloc init. With this singleton, all references to it should be through its sharedTimeFormat method, which will init the object if necessary, and will return the singleton instance otherwise.
In other words, it doesn't appear that you're referencing the instance of the object stored in the static sharedObject variable, which means that it's stored value will not necessarily be the same.

IOS7 Singleton Object Bad Access Code 1

(CODE UPDATED) after IOS 7 update, my app is crashing with singleton label (and this happened only in the device)...first time accessing singleton everything it's ok, but second time ClassSingleton is nil. Can anyone help? (before IOS 7 everything was fine...now i get Bad Access Code =1)
I'm using ARC...
thanks
ClassSingleton.h
#property (nonatomic, strong) IBOutlet UILabel *lblResultado;
ClassSingleton.m
#synthesize lblResultado;
__strong static ClassSingleton *pOutClassSingletonReturn = nil;
#pragma mark Singleton Methods
+ (void)initialize
{
pOutClassSingletonReturn = [[super allocWithZone:NULL] init];
pOutClassSingletonReturn.lblResultado = [[UILabel alloc] init];
pOutClassSingletonReturn.lblResultado.backgroundColor = [UIColor clearColor];
pOutClassSingletonReturn.lblResultado.textColor = [UIColor whiteColor];
pOutClassSingletonReturn.lblResultado.textAlignment = NSTextAlignmentRight;
pOutClassSingletonReturn.lblResultado.text = #"0";
}
+ (ClassSingleton*) pOutClassSingletonReturn
{
return pOutClassSingletonReturn;
}
#end
AccessClass.m
#implementation AccessClass
__strong static ClassSingleton *pOutClassSingletonReturn;
- (void)viewDidLoad
{
[super viewDidLoad];
externalsObjects = [NSDictionary dictionaryWithObject:[ClassSingleton pOutClassSingletonReturn] forKey:#"pOutClassSingletonReturn"];
nibOptions = [NSDictionary dictionaryWithObject:externalsObjects forKey:UINibExternalObjects];
[self.nibBundle loadNibNamed:self.nibName owner:self options:nibOptions];
pOutClassSingletonReturn = [ClassSingleton pOutClassSingletonReturn];
pOutClassSingletonReturn.lblResultado.text = #"1";
}
- (IBAction) button: (id) sender
{
pOutClassSingletonReturn.lblResultado.text = #"blabla"; //==>>> Crash second time i press the button
}
You implemented the singleton pattern improperly in modern Objective-C.
In this example, let's call your Singleton class method, sharedInstance. Initialize your singleton as follows:
+ (id)sharedInstance
{
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Then, in the same Singleton Class, put your initialization code in your init method
- (id)init
{
self = [super init];
if (self) {
// Your initialization code goes here
}
return self;
}
You can change the method from init to whatever you want. Just make sure to change the name in the sharedInstance class method.
To call your Singleton in your other classes, simply do the following:
[MySingletonClass sharedInstance]
The first time it's called, the init method in the Singleton will be set (which is obvious, as it's a Singleton).
Try initializing your singleton using the static + (void)initialize method instead.
See: What should my Objective-C singleton look like?
I don't see where you've declared pOutClassSingletonReturn in AccessClass.m, so it's hard to tell if it's a global variable, an instance variable, a reference to the pOutClassSingletonReturn in ClassSingleton.m, or what. But I suspect that the problem isn't so much with the pOutClassSingletonReturn in ClassSingleton.m as it is with the one in AccessClass.m. Make sure that's a strong reference, or at least add it to your view hierarchy in -viewDidLoad.
First i want to thanks to everyone who try to help!
I found the error (singleton was ok...)...the error was
when i concatenate 2 NSString's like that i get an error (further in the singleton):
pOutclassCalculadora.pstrOutParcela1 = [pOutclassCalculadora.pstrOutParcela1 stringByAppendingString: pOutclassCalculadora.pstrOutTeclaSender];
Now i'm doing like that and everything is ok (no bad access):
pOutclassCalculadora.pstrOutParcela1 = [NSString stringWithFormat:#"%#%#",pOutclassCalculadora.pstrOutParcela1,pOutclassCalculadora.pstrOutTeclaSender];
The big question...why this "stringByAppendingString" works in the simulator and in IO6 and crashes in IOS7 (and only in the device)?????
If you are using ARC then try declaring the static to be strong like this:
__strong static ClassSingleton *pOutClassSingletonReturn = nil;
so that ARC knows to retain it for you.

Singleton gets deallocated

I've created a simple singleton class to hold static data for my projects.
The first time I access this singleton is onEnter method in my Cocos2d scene. However when I try to access it again later in another method (same scene) this singleton is already released. I'm confused, how do I keep my singleton from being deallocated?
Here's my singleton's interface part:
#import <Foundation/Foundation.h>
#interface OrchestraData : NSObject
+(OrchestraData *)sharedOrchestraData;
#property (retain, readonly) NSArray *animalNames;
#end
Implementation:
#import "OrchestraData.h"
#implementation OrchestraData
#synthesize animalNames = animalNames_;
+(OrchestraData*)sharedOrchestraData
{
static dispatch_once_t pred;
static OrchestraData *_sharedOrchestraData = nil;
dispatch_once(&pred, ^{ _sharedOrchestraData = [[OrchestraData alloc] init]; });
return _sharedOrchestraData;
}
-(id)init {
if (self = [super init]) {
animalNames_ = [NSArray arrayWithObjects:#"giraffe", #"giraffe", #"giraffe", #"giraffe", nil];
}
return self;
}
#end
I'm using my singleton this way:
[[OrchestraData sharedOrchestraData] animalNames];
Update:
I took a fresh look into it with NSZombies enabled, it appears as if my NSArrays were released, not the singleton itself. What do I do?
You must implement your singleton in this way:
1) in .h file of your Singleton Class:
+ (SingletonClass *)instance;
2) in .m file:
+ (SingletonClass *)instance {
static SingletonClass* instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
//your init code here
});
return instance;
}
If you want to call your singleton, just call [SingletonClass instance].
If your are interesting what is "dispatch_once_t", read about Grand Central Dispatch:
http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
RE Update:
Your NSArray deallocates because you're using the autorelease initializer arrayWithObjects and you assign it directly to the ivar animalNames_. Therefore it is not retained.
To fix this, assign the array to the property:
self.animalNames = [NSArray arrayWithObjects:#"giraffe", #"giraffe", #"giraffe", #"giraffe", nil];
Btw, under ARC this wouldn't have been an issue since the ivar would have been a strong (retaining) reference. I don't get tired encouraging anyone to switch to ARC. It's been available for well over a year now and there's absolutely no point in using MRC code anymore! It really pains me to see how developers still don't use the easier, faster, and straightforward option. (rant off) :)
You setup a pointer wherever you need it.
-(void)someMethod {
MySingleton *singleton = [MySingleton sharedSingleton];
singleton.data = YES; //dumb example to show you did something...
}
-(void)someOtherMethod {
MySingleton *singleton = [MySingleton sharedSingleton]; //You have to create a new pointer...
singleton.data = NO; //another dumber example to show you did something...
}
Note: this assumes that you have created a singleton the same way I have... your code might be different therefore causing my answer not to apply...
You need to overwrite the below method inside your singleton class, because in your program, if someone has initialised [[SingletonClass alloc] init] then singleton will have another instance and release it will cause an error.
+ (id)allocWithZone:(NSZone *)zone{
return [[self SingletonClass] retain];
}
- (id)copyWithZone:(NSZone *)zone{
return self;
}
- (id)retain{
return self;
}

Accessing NSDictionary in Singleton Class w/o Creating Copies

I have a singleton as follows, which creates an instance of NSDictionary to hold my data. Here is the .h:
#interface FirstLast : NSObject
#property (strong, nonatomic, readonly) NSArray *firstArray;
#property (strong, nonatomic, readonly) NSArray *lastArray;
#property (strong, nonatomic, readonly) NSDictionary *fl;
+ (FirstLast *) firstLast;
- (NSDictionary *) tempDic;
#end
Here is the .m
#implementation FirstLast
#synthesize firstArray = _firstArray;
#synthesize lastArray = _lastArray;
#synthesize fl = _fl;
+ (FirstLast *)firstLast {
static FirstLast *singleton;
static dispatch_once_t once;
dispatch_once(&once, ^{
singleton = [[FirstLast alloc] init];
NSLog(#"FirstLast instantiated");
});
return singleton;
}
- (NSDictionary *) tempDic{
_firstArray = [[NSArray alloc] initWithObjects:#"Bob", #"Joe", #"Sally", #"Sue", nil];
_lastArray = [[NSArray alloc] initWithObjects:#"Jones", #"Johnson", #"Thompson", #"Miller", nil];
_fl = [NSDictionary dictionaryWithObjects:_firstArray
forKeys:_lastArray];
NSLog(#"tempDic just made _fl at this address");
NSLog(#"%p", _fl);
return _fl;
}
#end
All of this works fine. In the view controller I instantiate all this for the first time (works fine too):
NSLog(#"VC is setting up tempDic");
[[WordsClues wordsClues] tempDic];
When I try to gain access to tempDic elsewhere, like this:
NSInteger rIndex = arc4random_uniform(4) + 1;
NSString *fname = [[[FirstLast firstLast].tempDic allValues] objectAtIndex:rIndex];
it works fine, but, when I repeat this process, each time I'm creating a new tempDic. I know this because the NSLog giving the address gives a different answer each time. I really want to access the existing dictionary, which is what I thought my singleton was going to accomplish. Clearly I'm either not accessing tempDic correctly or I misunderstand what the singleton can do for me or I have the tempDic set up wrong. The goal is to get a random value from a single copy of tempDic and not write local copies of tempDic all over the place. Thanks.
Why do you recreate the dictionary in -tempDic at all?
I.e. move the dictionary instantiation code to init and then just return _fl; in tempDic.
No worries -- we've all been there [new].
In your FirstLast class, implement the init method as something like:
- init
{
self = [super init];
if ( self ) {
_fl = ... create your dictionary here ...;
}
return self;
}
Then change -tempDic to:
- (NSDictionary*)tempDic {
return _fl;
}
I would highly recommend that you read a good intro to Objective-C book. I'm a purist and, thus, would recommend going to the source for the information, but there are lots of books available.
The questions you are asking are more in line with "What is object oriented programming and how does Objective-C work?".
To answer your question; FirstLast is a class and the singleton pattern makes sure there is exactly one instance of that class. By moving the creation of the dictionary to the init method -- which is called only once and who stores a reference to the created dictionary in an instance variable -- you avoid creating multiple dictionary instances.
Every time you call tempDic, you create a new copy of it. What you should do is add you code for creating the dictionary to your alloc instance, and then just retrieve it in your getter.
Alternativly you can do this
- (NSDictionary *) tempDic{
if( _fl == nil )
{
_firstArray = [[NSArray alloc] initWithObjects:#"Bob", #"Joe", #"Sally", #"Sue", nil];
_lastArray = [[NSArray alloc] initWithObjects:#"Jones", #"Johnson", #"Thompson", #"Miller", nil];
_fl = [NSDictionary dictionaryWithObjects:_firstArray
forKeys:_lastArray];
NSLog(#"tempDic just made _fl at this address");
NSLog(#"%p", _fl);
}
return _fl;
}

Singleton class DataLoader - set values from Core Data in AppDelegate, however cannot access DataLoader variables in other classes

I have been wracking my brain, and a lot of searching, but no luck so far.
Basically, I have setup Core Data to load data from an sqlite db in my AppDelegate class into shared variables in a singleton class called DataLoader.
I also have a main class called GameScene, which will also use DataLoader data.
What I am trying to do is as follows:
load data from DB (using the background thread) and store results in DataLoader singleton class variables (specifically an NSMutableArray)
In this way the data is loaded while the scenes transition to main menu area
In the GameScene class I try to access the data stored in the DataLoader singleton, and use this data in the game.
My issue is that although I can see that I store objects in the AppDelegate class, I don't seem able to return the objects in the GameScene class. Usually ends in an EXC_BAD_ACCESS error.
I am really looking for some example of something similar or if you have any ideas.
If you need code samples, let me know.
Thanks,
Pras.
*EDIT - Code Snippets*
DataLoader.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface DataLoader : NSObject {
}
//setup singleton
+(DataLoader *) sharedDataLoader;
#property (nonatomic, retain) NSMutableArray * veHint;//2 characters
DataLoader.m
#import "DataLoader.h"
#import <CoreData/CoreData.h>
#implementation DataLoader
#synthesize veHint;
static DataLoader * myDataLoader = nil;
+(DataLoader *) sharedDataLoader{
if(myDataLoader == nil){
myDataLoader = [[[DataLoader alloc]init]retain];
}
return myDataLoader;
}
-(id) init
{
if((self = [super init])){
veHint = [NSMutableArray arrayWithCapacity:10];
}
return self;
}
AppDelegate.mm
#import "AppDelegate.h"
#import "GameScene.h"
#import "RootViewController.h"
#import "Word.h"
#import "DataLoader.h"
-(void) applicationDidFinishLaunching:(UIApplication*)application
{
//other standard stuff here...
//my load data method using data loader
[self getRandomData];
//other standard stuff here...
// Run the intro Scene
[[CCDirector sharedDirector] runWithScene: [GameScene node]];
}
//other standard methods for Core Data
//Random data loader
-(void) fetchRandomData: (NSString *) searchInteger{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"fetching random data for word size: %#", searchInteger);
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Word" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
//setup predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY %K == %#", #"size", searchInteger];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
NSLog(#"fetch results count = %i", [fetchResults count]);
[request release];
NSMutableArray * results = [NSMutableArray arrayWithCapacity:10];
NSMutableArray * fetchedIndexes = [NSMutableArray arrayWithCapacity:10];
//get the total count, randomize selection of 10 results from the list
int j = 0;
while (j<10) {
int index = arc4random() % [fetchResults count];
//get only unique values
if ([fetchedIndexes containsObject:[NSNumber numberWithInt:index]] == false) {
[fetchedIndexes addObject:[NSNumber numberWithInt:index]];
Word * word = (Word *) [fetchResults objectAtIndex:index];
//NSLog(#"j = %i, index = %i, wordsize = %#, wordstring = %#",j, index, word.size, word.wordString);
[results addObject:word];
j++;
}
}
//set dataloader
[[DataLoader sharedDataLoader] setVeHint:results];
[pool drain];
}
Also, need to mention that the EXC_BAD_ACCESS is on the [pool drain] call in AppDelegate.mm.
Thanks in advance for your help.
The problem is probably somewhere in the code (I think it is memory/scope-related, like too early release etc.). It really shouldn't matter how you access your data as long as you're keeping every piece of it properly "memory managed".
I can show you how I do this kind of data management.
I usually name it DataManager:
DataManager.h
#import <Foundation/Foundation.h>
#interface DataManager : NSObject {
NSDate *someDate;
}
#property (nonatomic, retain) NSDate *someDate;
+(DataManager*)sharedInstance;
- (void)loadSettings;
- (void)saveSettings;
#end
DataManager.m
#import "DataManager.h"
static DataManager *dataManagerInstance;
#implementation DataManager
#synthesize someDate = _someDate;
// singleton method
+(DataManager*)sharedInstance {
if(!dataManagerInstance) {
dataManagerInstance = [[DataManager alloc] init];
}
return dataManagerInstance;
}
- (void)saveSettings {
// save your data
}
- (void)loadSettings {
// load your data
}
I hope you got the idea… maybe you post some code snippets so we can find what's going wrong with your singleton ;-)
Update: OK, so you added code snippets and the problem is obviously in this line:
[[DataLoader sharedDataLoader] setVeHint:results];
You are setting veHint to be results, so you are overwriting your private variable of DataLoader. You shouldn't do it that way, since results is released and DataLoader gets lost of the reference.
You should either write another setter method, which makes a copy of results and puts the elements in veHint
or
retain results after passing it to DataLoader like this:
[[DataLoader sharedDataLoader] setVeHint:[results retain]];
However, this solution (and also yours) creates a memory leak, since you retained veHint in DataLoader and overwrite it in your Delegate (you completely lose the reference to veHint).
Core Data is not thread safe by default. If you are trying to use it on multiple threads, make sure you are familiar with Apple's guidelines.