Singleton set in AppDelegate loses it value when allocated in another class - objective-c

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.

Related

expected ';' at end of declaration list objective c

"error: expected ';' at end of declaration list" objective c
When I am trying to declare global variable
#implementation CachingManager{
NSMutableArray*object = [[NSMutableArray alloc] init];
}
You can create instance variables here but cannot initialize those instance variables here like you do. They are all initialized to nil or zeroes. So compiler expect a semicolon when you are writing an equal sign.
You can initialize them in init method or other method where your class initialization takes place in order to make them global variables. e.g.
Interface block for instance variable inside .m file:
#interface CachingManager ()
{
// instance variables initialized to nil or zeroes
NSMutableArray *object; // global ivar
}
#end
Implementation part same .m file:
#implementation CachingManager
- (void)viewDidLoad {
[super viewDidLoad];
object = [[NSMutableArray alloc] init]; // initialization takes place
}
One way to implement global variables, and to manage their lifetime (i.e. that they are initialised) and even to provide global methods is to implement a class exposing those variables/methods and to use the singleton pattern:
yourFile.h:
#import <Foundation/Foundation.h>
#interface GlobalVars : NSObject
{
NSMutableArray *_truckBoxes;
NSMutableArray *_farmerlist;
NSString *_farmerCardNumber;
NSString *_fName;
}
+ (GlobalVars *)sharedInstance;
#property(strong, nonatomic, readwrite) NSMutableArray *truckBoxes;
#property(strong, nonatomic, readwrite) NSMutableArray *farmerList;
#property(strong, nonatomic, readwrite) NSString *farmerCardNumber;
#property(strong, nonatomic, readwrite) NSString *fName;
#end
yourFile.m:
#import "GlobalVars.h"
#implementation GlobalVars
#synthesize truckBoxes = _truckBoxes;
#synthesize farmerList = _farmerList;
#synthesize frameCardNumber = _frameCardNumber;
#synthesize fName = _fName;
+ (GlobalVars *)sharedInstance {
static dispatch_once_t onceToken;
static GlobalVars *instance = nil;
dispatch_once(&onceToken, ^{
instance = [[GlobalVars alloc] init];
});
return instance;
}
- (id)init {
self = [super init];
if (self) {
_truckBoxes = [[NSMutableArray alloc] init];
_farmerlist = [[NSMutableArray alloc] init];
// Note these aren't allocated as [[NSString alloc] init] doesn't provide a useful object
_farmerCardNumber = nil;
_fName = nil;
}
return self;
}
You can then use these global variables like this, for example:
GlobalVars *globals = [GlobalVars sharedInstance];
globals.fName = #"HelloWorld.txt";
[globals.farmerList addObject:#"Old Macdonald"];
[self processList:[globals farmerList]];
However, please consider:
You don't need to use global variables like this; you should be able to create a model object which is created as necessary and reference to it passed to the views. This is MVC.
You also posted a stack trace of an unrelated issue which is extremely common with Objective-C; only you can fix this error, once you realise what it is.
That sin't a global variable. That would be an instance variable and that particular syntax wasn't commonly used after 2005 (really, it wasn't terribly common after the mid 90s).
If you want a global variable, do:
NSMutableArray *myGlobal;
Somewhere at the top level -- at the same level with the #implementation -- of your source.
You'll have to initialize the global variable elsewhere, though. Typically, in the +initialize or +load method of the class.

Proper subclassing of SKSpriteNode and instance initialization

I am trying to subclass SKSpriteNode so that I can use it to create different characters with different properties for a game. I know this should be a trivial task, but I am having a hard time understanding how to initialize a variable when instances are created.
In the header I have:
#import <SpriteKit/SpriteKit.h>
#interface MyClass : SKSpriteNode
#property int myVariable;
#end
And in the implementation:
#import "MyClass.h"
#interface MyClass ()
#end
#implementation MyClass
#synthesize myVariable;
- (id)init {
if (self = [super init]) {
myVariable = 100;
}
return self;
}
When I create the node in the scene:
#implementation GameScene
{
MyClass *mySprite;
}
- (void)didMoveToView: (SKView *) view
{
mySprite = [MyClass spriteNodeWithImageNamed:#"image.png"];
}
The value of myVariable is 0, which I assume it means that spriteNodeWithImageNamed does not execute the init method. If I use:
mySprite = [[MyClass alloc] init]; // or mySprite = [MyClass new];
mySprite = [MyClass spriteNodeWithImageNamed:#"image.png"];
The variable name is correctly set to 100, but then reverted to 0 by spriteNodeWithImageNamed.
I have also tried:
mySprite = [[MyClass alloc] init]; // or mySprite = [MyClass new];
[mySprite setTexture:[SKTexture textureWithImageNamed:#"image.png"]];
In this case the value of myVariable is correct, but no image appears when I add the node to the scene. What init method is called when spriteNodeWithImageNamed is used? Am I improperly overriding the SKSpriteNode init method?
You have done everything correctly with creating a class, but you need to keep in mind that you are calling the factory method spriteNodeWithImageNamed, which then initializes and allocates by itself. Thus, if you want to use your custom init method you need to override the factory method as well.
Inside of your implementation of MyClass, override the method to call your initializer:
+ (instancetype) spriteNodeWithImageNamed:(NSString *)imageName{
MyClass *newClass = [[MyClass alloc] init];
SKTexture *spriteTexture = [SKTexture textureWithImageNamed:imageName];
newClass.size = spriteTexture.size;
newClass.texture = spriteTexture;
return newClass;
}
Hope this helps, let me know if you have questions.
*EDIT: Im actually a little tempted to ditch this, only because SKSpriteNode doesn't have an init method (or at least not that we know of). So unless you want to dive deep into Apple's mysteries then just do it this way:
+ (instancetype) spriteNodeWithImageNamed:(NSString *)imageName{
MyClass *newClass = [[MyClass alloc] initWithImageNamed:imageName];
newClass.myVariable = 100;
return newClass;
}
You could instead override initWithImage to put in myVariable, but the whole point is that I'd avoid trying to use the init initializer with SKSpriteNode.
Here is how I do it. Create your own custom init method, and simply call the default spritenode initWithImage.
-(id)initShipWithType:(ShipType)type {
self = [super initWithImageNamed:[self getShipSprite:type]];
if (self) {
}
return self;
}

Appending to an array in an object from a ViewController

I would like to append an object to an NSMutableArray in an object class from a ViewController. It's set up like bellow, but the code below does not seem to work. If I log the array from the ViewController, it appears to be appended, but if I log it from the object class, it's empty.
CaptureManager.h
#import <UIKit/UIKit.h>
#class AVCamRecorder;
#protocol CaptureManagerDelegate;
#interface CaptureManager : NSObject {
}
#property (nonatomic,strong) NSMutableArray *assets;
#end
ViewController
CaptureManager *cm = [[CaptureManager alloc] init];
cm.assets = [[NSMutableArray alloc]init];
[cm.assets addObject:asset];
Alternatively, just pass in a specific instance of CaptureManager to the VC, either through an initializer or by creating a CaptureManager property on the VC and setting it to the specific instance of CaptureManager.
You can (and should) read all about why you should avoid singleton abuse here.
It's because in your view controller you're creating a new object of CaptureManager. So you must pass a pointer of already created CaptureManager and use that, or have a shared instance (singleton) and use that in your view controllers, e.g.
#interface CaptureManager : NSObject
+ (instancetype)sharedManager;
#property (nonatomic,strong) NSMutableArray *assets;
#end
//--
#implementation
+ (instancetype)sharedManager
{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [self new];
});
return instance;
}
- (id)init
{
self = [super init];
self.assets = [NSMutableArray new];
return self;
}
Then, in your vc:
[[CaptureManager sharedManager].assets addObject:asset];

How to init objects in Objective-C [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm unsure how I should initialise the various properties in an objective-C class. Please assume I'm a very new user to Objective-C in your answers...
I have the following classes:
Test class
#interface Test : NSObject
#property (nonatomic, strong) NSString *name;
#end
TestManager class
#interface TestManager : NSObject
#property (nonatomic, strong) NSMutableArray *tests; // array of Test objects (array size unknown until runtime)
#end
Controller class
#interface TestController : NSObject
#property (nonatomic, strong) TestManager *aManager;
-(void)initManager;
-(void)doSomething;
#end
I want to have an method like initManager called:
-(void)initManager
{
// how can I init the aManager which will have an array of Test objects
}
which will automatically allocate an array of objects to be stored inside the manager class so I can do things like:
-(void)doSomething
{
NSString *name = ((Test *)[self.aManager.tests objectAtIndex:0]).name;
}
I'm not even sure that initManager is the correct method to use - is there something built in that always gets called?
Firstly, let's look at the way we can initialize your Test class objects.
You can also write some initialization method for your Test class so instead of this:
Test example = [[Test alloc] init];
example.name = #"s";
you can write something like this:
Test example = [[Test alloc] initWithName:#"s"];
Please note that this is very common for initialization method to return newly created object, hence the initialization method usually returns 'id' type (not void).
This is the implementation for your test class which will be used in examples below.
.h file:
- (id)initWithName:(NSString *)aName;
.m file:
- (id)initWithName:(NSString *)aName
{
self = [super init];
if (self) {
_name = aName;
}
return self;
}
You can initialize your TestController class this way:
.h file:
- (id)initManager;
.m file:
- (id)initManager
{
self = [super init]; //always call the superclass init method when your class inherit from other class
if (self) { // checking if the superclass initialization was fine
_tests = [NSMutableArray array];
[_tests addObject:[[Test alloc] initWithName:#"s"]];
[_tests addObject:[[Test alloc] initWithName:#"l"]];
}
return self;
}
Or something like this:
- (id)initManager
{
self = [super init]; //always call the superclass init method when your class inherit from other class
if (self) { // checking if the superclass initialization was fine
_tests = [NSArray arrayWithObjects:[[Test alloc] initWithName:#"s"], [[Test alloc] initWithName:#"l"]];
}
return self;
}
Like the #Andrew said it is better to use alloc + init. Here are some examples of this syntax:
CGRect rect = CGRectMake(0, 0, 100, 100);
[[UIView alloc] initWithFrame:rect];
[[NSArray alloc] init]
This is the common way to initialize objects. Despite having this mechanism there are also some additional methods (which are in fact static functions) which give the programmer the nice way to initialize objects. Using them u don't have to write keyword 'alloc' so that the code is shorter and easier to read.
[NSArray array] //creates and returns empty array
[NSMutableArray array] //creates and return empty mutable array
[UIButton buttonWithType:UIButtonTypeContactAdd]; //creates and return button
first import header files of test, and test manager class, into controller class
#import Test.h
#import TestManager.h
then in controller class
-(void)initManager
{
TestManager *aTestManager = [TestManager new];
Test *test1 = [Test new];
Test *test2 = [Test new];
[aTestManager.tests addObject:test1];
[aTestManager.tests addObject:test2];
}
Let's start at the top. You probably can and should make the name readonly.
(Demos assume ARC is enabled)
#interface Test : NSObject
#property (nonatomic, readonly) NSString *name;
// and then simply initialize name:
- (instancetype)initWithName:(NSString *)pName;
#end
NSString properties should be copied:
#implementation Test
- (instancetype)initWithName:(NSString *)pName
{
self = [super init];
if (nil == self) return nil;
// copy the NSString:
// don't use getters/setters in initializers or -dealloc
_name = pName.copy;
return self;
}
#end
Similarly readonly:
#interface TestManager : NSObject
#property (nonatomic, strong, readonly) NSMutableArray *tests; // array of Test objects (array size unknown until runtime)
#end
#implementation TestManager
- (id)init
{
self = [super init];
if (nil == self) return nil;
// just initialize readonly tests:
_tests = NSMutableArray.new;
return self;
}
#end
Then TestController could probably use a readonly TestManager and borrow the form used above. Otherwise, it can be readwrite, if needed.
// don't declare/implement an instance method
// which has the prefix -init*, like initManager. renamed.
- (void)resetManager
{
// if readonly is ok, then just create it in the initializer.
// otherwise, if you need the ability to set the manager in the controller,
// then declare the property readwrite and:
self.testManager = TestManager.new;
// note: aManager is not a good name. renamed to testManager.
}
- (void)doSomething
{
assert(self.testManager && "did you forget to init the manager?");
Test * test = [self.testManager.tests objectAtIndex:0];
NSString * name = test.name;
...
}
This is far from covering all initialization cases in ObjC, but it is a start.

Global variable NSMuteableArray using Singleton Class

I'm having trouble creating a nice way of passing a collection around to different view controllers. For example, I created a custom class called Message with a bunch of attributes. I want to have a global NSMutableArray of those stored in a global variable of sorts called messages that I can add to or get from anywhere. Everyone on Stackoverflow says not to use your delagate class to store global variables so I created a singleton class called Shared. In there I created a property for the NSMutableArray called messages like this:
#interface Shared : NSObject {
}
#property (nonatomic, retain) NSMutableArray *messages;
+(Shared *) sharedInstance;
#end
And my .h file is (the important part):
#import "Shared.h"
static Shared* sharedInstance;
#implementation Shared
#synthesize messages;
static Shared *sharedInstance = nil;
-(id) init {
self = [super init];
if (self != nil){
}
return self;
}
-(void) initializeSharedInstance {
}
+ (Shared *) sharedInstance{
#synchronized(self) {
if (sharedInstance == nil){
sharedInstance = [[self alloc] init];
[sharedInstance initializeSharedInstance];
}
return (sharedInstance);
}
}
In my other view controller, I first import "Shared.h", then try this:
[[Shared sharedInstance].messages addObject:m];
NSLog([NSString stringWithFormat:#"Shared messages = %#", [Shared sharedInstance].messages]);
It keeps printing null instead of the the collection of m objects. Any thoughts?
You need to have a static variable.
In .h:
#interface Shared : NSObject
{
NSMutableArray *messages;
}
#property (nonatomic, retain) NSMutableArray *messages;
+ (Shared*)sharedInstance;
#end
in .m:
static Shared* sharedInstance;
#implementation Shared
#synthesize messages;
+ (Shared*)sharedInstance
{
if ( !sharedInstance)
{
sharedInstance = [[Shared alloc] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if ( self )
{
messages = [[NSMutableArray alloc] init];
}
return self;
}
A thought:
#synthesize generates setter and getter methods, it doesn't init your variable. Where do you do that? I can't see it in the excerpts you posted.
The following is not an answer to your issue, but instead a suggestion to an alternative approach that (in my opinion) is 'cleaner' in use.
An alternative to using a Singleton to store app-wide could be to define a class with class methods that retrieves values from the NSUserDefaults. This class could be imported into the prefix header (*.pch) so you can access it from every other class in the project.
Methods inside this class could look like this:
inside Settings.h:
// for this example I'll use the prefix for a fictional company called SimpleSoft (SS)
extern NSString *kSSUserLoginNameKey;
+ (NSString *)userLoginName;
+ (void)setUserLoginName:(NSString *)userLoginName;
inside Settings.m:
kSSUserLoginNameKey = #"SSUserLoginNameKey";
+ (NSString *)userLoginName
{
return [[NSUserDefaults standardUserDefaults] valueForKey:kSSUserLoginNameKey];
}
+ (void)setUserLoginName:(NSString *)userLoginName
{
[[NSUserDefaults standardUserDefaults] setValue:userLoginName forKey:kSSUserLoginNameKey];
[[NSUserDefaults standardUserDefaults] synthesize];
}
Of course in a setup like this NSUserDefaults is the singleton that is being accessed through a convenience class. This class acts as a wrapper around the NSUserDefaults singleton. Values can be accessed like this:
NSString userLoginName = [Settings userLoginName];
[Settings setUserLoginName:#"Bob"];
Other objects -like Arrays- could be accessed in much the same way. One thing to be careful with (much the same as with your current approach) is to be careful not to access a class like this from every other class. Components that are intended to be reusable should pass values, so the components of the application don't become too tightly coupled to the settings class.