Why is my Singleton not maintaining it's set value? - objective-c

I have an app where I'm using a singleton; here is the code in the .h file:
#interface SingletonServicesType : NSObject {
}
#property (nonatomic, retain) NSNumber *globalServicesType;
+ (id)sharedInstance;
#end
Here is the code in the .m file:
//-------------------------------------------
//-- SingletonServicesType
#implementation SingletonServicesType {
}
#synthesize globalServicesType; // rename
//-- sharedInstance --
+ (id)sharedInstance {
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) {
globalServicesType = [[NSNumber alloc] init];
}
return self;
}
#end
Here is the code where I set the initial value of singleton in AppDelegate.m:
// set services
SingletonServicesType *sharedInstance = [SingletonServicesType sharedInstance]; // initialize
if(preferenceData.aServicesType == nil) {
sharedInstance.globalServicesType = 0; // (0) is the default
preferenceData.aServicesType = 0; // here too...
[localContext MR_saveNestedContexts]; // save it...
}
else
sharedInstance.globalServicesType = preferenceData.aServicesType; // 0 = default (nails), 1 = custom
NSLog(#"\n\n1-sharedInstance.globalServicesType: %#", [NSNumber numberWithInt: (sharedInstance.globalServicesType)]); // shows a value of '0'
When I immediately examine the value of the singleton in another class, it's 'null'! Here's that code:
SingletonServicesType *sharedInstance = [SingletonServicesType sharedInstance]; // initialize
NSLog(#"\n\n2-sharedInstance.globalServicesType: %#", sharedInstance.globalServicesType); // shows a value of 'null'
I don't understand why the value is maintaining the setting? Am I missing something?

This is because you are assigning zero to NSNumber*. You need to assign [NSNumber numberWithInt:0] or #0, otherwise the integer is interpreted as an address:
sharedInstance.globalServicesType = #0; // <<== Here

Related

iOS(Objective-C) Crashed for EXC_BAD_ACCESS,when get and set a atomic Global variable in Multithreading

My app crashed, suspected to be caused by multi-threaded operation of an attribute in a singleton object.
So I wrote a small piece of code and successfully reproduced the problem, but I still couldn't understand it.
I have defined the property as #property, which is atomic. Why does it still crash when accessed by multiple threads? Below is my code snippet:
Audio.h
#interface Audio : NSObject
#property NSString *audioName;
#property NSString *audioData;
#end
Audio.m
#import "Audio.h"
#implementation Audio
- (instancetype)init{
self = [super init];
if (self) {
_audioData = #"";
_audioName = nil;
}
return self;
}
#end
AudioManager.h
#interface AudioManager : NSObject
+(instancetype)shareInstance;
#property Audio *curAudio;
-(void) play;
-(void) clearCurAudio;
#end
AudioManager.m
#import "AudioManager.h"
#implementation AudioManager
static id sharedInstance = nil;
+(instancetype)shareInstance {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
-(void) play {
NSLog(#"Current Audio name : %#",_curAudio.audioName);
NSLog(#"Current Audio name : %#",_curAudio.audioData);
NSLog(#"Current Audio name : %#",_curAudio.audioName);//crahed here!
NSLog(#"Current Audio name : %#",_curAudio.audioData);
}
-(void) clearCurAudio {
_curAudio = nil;
}
#end
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
dispatch_queue_t thread1 = dispatch_queue_create("queue1", nil);
dispatch_queue_t thread2 = dispatch_queue_create("queue2", nil);
dispatch_queue_t thread3 = dispatch_queue_create("queue3", nil);
dispatch_async(thread1, ^{
for (int i = 0; i < 1000; i++) {
Audio *newAudio = [[Audio alloc] init];
newAudio.audioName = #"na";
[[AudioManager shareInstance] setCurAudio:newAudio];
}
});
//
dispatch_async(thread2, ^{
for (int i = 0; i < 1000; i++) {
[[AudioManager shareInstance] play];
}
});
//
dispatch_async(thread3, ^{
for (int i = 0; i < 1000; i++) {
AudioManager * audioManager = [AudioManager shareInstance];
[[AudioManager shareInstance] clearCurAudio];
}
});
}
Here is the crash EXC_BAD_ACCESS:
enter image description here
Thank you guys,problem solved!!, as #Willeke posted.
use self.curAudio rather than _curAudio.

objective c singleton return nil

I have singleton to use websocket connection, when _srWebSocket = nil, will returned new _srWebSocket, but when _srWebSocket connected, will returned nil
#interface SocketManager : NSObject
#property (nonatomic, weak) SRWebSocket *srWebSocket;
+ (SocketManager *) sharedInstance;
#end
#implementation SocketManager
#synthesize srWebSocket = _srWebSocket;
+ (SocketManager *) sharedInstance {
static SocketManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SocketManager alloc] init];
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
NSLog(#"In SocketKeeperSingleton init");
}
return self;
}
- (SRWebSocket *) srWebSocket {
if (!_srWebSocket || _srWebSocket.readyState == SR_CLOSED) {
_srWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:#"wss://...:8000"]];
[_srWebSocket open];
}
return _srWebSocket;
}
#end
Call singleton
srWebSocket = [SocketManager sharedInstance].srWebSocket;
because you have write 'static SocketManager *sharedInstance = nil;' in shareInstance Method .
when this method call every time they send nil .
+ (SocketManager *) sharedInstance {
static SocketManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SocketManager alloc] init];
});
return sharedInstance;
}
write down this line outside shareInstance method "static SocketManager *sharedInstance = nil;"
You are missing srWebSocket method call,
First way,
Declare one method in singleton.h file
-(void) doInitialSetupOnLaunch;
Add this method somewhere in your singleton.m file,
-(void) doInitialSetupOnLaunch {
//Put your initialized code i mean you can call your method from here & define your properties here.
_srWebSocket = [self srWebSocket];
}
- (SRWebSocket *) srWebSocket {
if (!_srWebSocket || _srWebSocket.readyState == SR_CLOSED) {
_srWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:#"wss://...:8000"]];
[_srWebSocket open];
}
return _srWebSocket;
}
Call it from somewhere,best way you can call from appDelegate.m
Now you are ready to access it by property,
Call singleton
srWebSocket = [SocketManager sharedInstance].srWebSocket;
Or Second way
SRWebSocket * srWebSocket = [[SocketManager sharedInstance] srWebSocket];

Object loses reference

I have a IKImageBrowserView which has its own controller - BrowserController.h + .m
#interface BrowserController : NSWindowController{
NSMutableArray *_images;
}
#property (strong,nonatomic) IBOutlet IKImageBrowserView *imageBrowser;
-(void)addMultipleImages;
When I run the app for the first time, the staring image loads, but when I click a button to add other images and call a method from another class I don't get any results. I have noticed that my _imageBrowser loses the reference and becomes nil.
How could I solve this issue?
AppDelegate.m
#import "AppDelegate.h"
#import "BrowserController.h"
#implementation AppDelegate{
BrowserController *imageBrowserController;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
imageBrowserController = [BrowserController sharedManager];
}
- (IBAction)doSomething:(id)sender {
[imageBrowserController addMultipleImages];
}
#end
BrowserController.m
#import "BrowserController.h"
#interface myImageObject : NSObject
{
NSString *_path;
}
#end
#implementation myImageObject
/* our datasource object is just a filepath representation */
- (void)setPath:(NSString *)path
{
if(_path != path)
{
_path = path;
}
}
/* required methods of the IKImageBrowserItem protocol */
#pragma mark -
#pragma mark item data source protocol
/* let the image browser knows we use a path representation */
- (NSString *)imageRepresentationType
{
return IKImageBrowserPathRepresentationType;
}
/* give our representation to the image browser */
- (id)imageRepresentation
{
return _path;
}
/* use the absolute filepath as identifier */
- (NSString *)imageUID
{
return _path;
}
#end
#interface BrowserController ()
#end
#implementation BrowserController
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void)awakeFromNib{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
- (void)updateDataSource{
[_imageBrowser reloadData];
}
-(NSUInteger) numberOfItemsInImageBrowser:(IKImageBrowserView *)aBrowser{
return [_images count];
};
-(id)imageBrowser:(IKImageBrowserView *)aBrowser itemAtIndex:(NSUInteger)index{
return [_images objectAtIndex:index];
};
- (void)updateDatasource
{
[_imageBrowser reloadData];
}
- (void)addMultipleImages{
myImageObject *p;
p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[_images addObject:p];
[_images addObject:p];
[_imageBrowser reloadData];
}
+ (id)sharedManager {
static BrowserController *sharedMyManager = nil;
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
#end
There is some confusion as to the name of the class where you mention it's called ImageBrowserController at the start of your question and it looks like it's called BrowserController at the end of your question.
The issue is that you allocate _images in awakeFromNib which is never called given the class is created via the singleton pattern (sharedInstance) and not loaded from a .nib file.
Therefore move the code from awakeFromNib into init and dump awakeFromNib:
BrowserController.m:
- (id)init
{
self = [super init];
if (self) {
myImageObject *p = [[myImageObject alloc]init];
[p setPath:[[NSBundle mainBundle]pathForResource:#"Unknown" ofType:#"jpg"]];
_images = [[NSMutableArray alloc]init];
[_images addObject:p];
[self updateDataSource];
}
return self;
}
Further confusion: you have implemented an initWithFrame method. Why?

Casting in Objective-C does not work as expected

I am new to programming with Objective-C and Stackoverflow, and I need some help.
I'm trying to get an object from a NSMutableArray and check one of its internal properties. I figured the best way to do this is by doing the following cast:
GenericRoom *room = (GenericRoom*)[myRooms objectAtIndex: currentIndex + side];
if (room.myType == EMPTY) {
return YES;
}
The problem is that when I create a class of type GenericRoom, your constructor already defines myType as EMPTY. When I insert the instances in NSMutrableArray, I changed to myType TAVERN this way:
NSMutableArray *myRooms = [[NSMutableArray alloc] initWithCapacity:15];
for (int i = 0: i <15: i + +) {
    GenericRoom tmpRoom * = [[GenericRoom alloc] initWithType: TAVERN];
    myRooms insertObject: tmpRoom atIndex i];
}
But when I do the cast that quote up there, it simply creates a new instance of the object GenericRoom, instead of copying the object inside NSMutableArray, making its result is always YES, my failing vereficação.
Is there any better way to solve this problem?
Thank you all.
EDIT: The complete code
GenericRoom.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
enum Rooms {
EMPTY, TAVERN, WARRIORGUILD, MAGEGUILD
}roomsType;
#interface GenericRoom : CCLayer {
CCSprite *mySprite;
enum Rooms myType;
}
#property (nonatomic, retain) CCSprite *mySprite;
#property enum Rooms myType;
#property int test;
- (id)initWithSprite: (NSString *)file;
- (id)initWithType: (enum Rooms)roomType;
#end
GenericRoom.m
#import "GenericRoom.h"
#implementation GenericRoom
#synthesize mySprite, myType, test;
-(id)init {
if (self = [super init]) {
}
return self;
}
-(id)initWithType: (enum Rooms) roomType {
if (self = [super init]) {
myType = roomType;
}
return self;
}
-(id)initWithSprite: (NSString *)file {
if (self = [super init]) {
mySprite = [CCSprite spriteWithFile:file];
[self addChild: mySprite];
}
return self;
}
#end
RoomManager.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "GenericRoom.h"
#import "RoomTavern.h"
#import "RoomEmpty.h"
enum Sides {
LEFT, RIGHT, UP, DOWN
}theSides;
#interface RoomManager : CCLayer {
CGSize size;
NSMutableArray *myRooms;
}
- (void) CreateRoom: (enum Rooms)roomType;
#end
RoomManager.m
#implementation RoomManager
-(id)init {
if (self = [super init]) {
size = [[CCDirector sharedDirector] winSize];
myRooms = [[NSMutableArray alloc] initWithCapacity:15];
for (int i = 0; i < 15; i++) {
GenericRoom *tmpRoom = [[GenericRoom alloc] initWithType:TAVERN];
tmpRoom.test = 10;
[myRooms insertObject:tmpRoom atIndex:i];
}
[self CreateRoom:TAVERN];
}
return self;
}
- (void) CreateRoom: (enum Rooms)roomType {
switch (roomType) {
case TAVERN:
{
//Create the Tavern Main Room
RoomTavern *tmpRoom = [[RoomTavern alloc] initWithSprite:#"room-hd.png"];
tmpRoom.mySprite.position = ccp(size.width/2,size.height/2);
[self addChild:tmpRoom];
[myRooms removeObjectAtIndex:8];
[myRooms insertObject:tmpRoom atIndex:8];
if ([self CheckAdjacentRooms:8 andSide:LEFT]) {
RoomEmpty *tmpEmptyRoom = [[RoomEmpty alloc] initWithSprite:#"roomToBuild-hd.png"];
tmpEmptyRoom.mySprite.position = ccp(tmpRoom.mySprite.position.x - tmpRoom.mySprite.contentSize.width, tmpRoom.mySprite.position.y);
[self addChild:tmpEmptyRoom];
[myRooms insertObject:tmpEmptyRoom atIndex:7];
}
break;
}
default:
break;
}
}
- (BOOL) CheckAdjacentRooms: (int)currentIndex andSide:(enum Sides)side {
int leftRightSide = 0;
if(side == LEFT)
leftRightSide = -1;
else if (side == RIGHT)
leftRightSide = 1;
GenericRoom *roomTmp = (GenericRoom *)[myRooms objectAtIndex:currentIndex + side];
if (roomTmp.myType == EMPTY) {
return YES;
}
else
return NO;
}
#end
To enhance #Dan F answer. Read this ...
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html#//apple_ref/doc/uid/TP30001163-CH11-SW7
... what's nil messaging. Your locally declared myRooms variable hides your myRooms property (= instance variable). In other words, myRooms is nil. And what happens ...
GenericRoom *room = (GenericRoom*)[myRooms objectAtIndex: currentIndex + side];
Because of nil messaging rules, [myRooms objectAtIndex:...] returns nil, thus your room is set to nil.
if (room.myType == EMPTY) {
return YES;
}
And room.myType sends message myType to room object. And because room is nil, return value is 0 - enums does contain integer scalar values - this is the reason why it returns 0. And I assume that EMPTY is first element in your enum = has value 0. And 0 == 0 => returns YES even when your myRooms instance variable is nil.
If your code above is how it appears in your actual project, then the problem is you are re-declaring your myRooms object. When you write this line:
NSMutableArray *myRooms = [[NSMutableArray alloc] initWithCapacity:15];
you are creating a new locally-scoped object named myRooms that is lost when you exit the current scope (method, block, etc.)
What you need to do to fix this is simply remove the NSMutableArray * at the beginning of the line, and it will assign the newly allocated array to your property. Like so:
myRooms = [[NSMutableArray alloc] initWithCapacity:15];
Your initialiser seems to have some flaws, that might cause your issues.
// NOTE: `Rooms` instead of `enum Rooms`
- (id)initWithType:(Rooms)roomType
{
if (self = [super init])
{
// NOTE: accessor is used, otherwise the value is cleared when out of scope
self.myType = roomType;
}
return self;
}

Init a NSMutableArray in a singelton that saves as a state

Im am using a pattern from the book Learning Cocos2D. I have a GameState class that is a singleton that can be saved as a state. The BOOL soundOn gets init as false the first time the class gest created, but the array levelProgress dosent. I made comments on all the lines on which I have tried to init the array.
The class uses a helper that loads and saves the data.
When i try 1 or 2 i get "instance variable 'levelProgress' accessed in class method" error.
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface GameState : NSObject <NSCoding> {
BOOL soundOn;
NSMutableArray *levelProgress;
}
+ (GameState *) sharedInstance;
- (void)save;
#property (assign) BOOL soundOn;
#property (nonatomic, retain) NSMutableArray *levelProgress;
#end
#import "GameState.h"
#import "GCDatabase.h"
#implementation GameState
#synthesize soundOn;
#synthesize levelProgress;
static GameState *sharedInstance = nil;
+(GameState*)sharedInstance {
#synchronized([GameState class])
{
if(!sharedInstance) {
sharedInstance = [loadData(#"GameState") retain];
if (!sharedInstance) {
[[self alloc] init];
// 1. levelProgress = [[NSMutableArray alloc] init];
}
}
return sharedInstance;
}
return nil;
}
+(id)alloc
{
#synchronized ([GameState class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a \
second instance of the GameState singleton");
sharedInstance = [super alloc];
// 2. levelProgress = [[NSMutableArray alloc] init];
return sharedInstance;
}
return nil;
}
- (void)save {
saveData(self, #"GameState");
}
- (void)encodeWithCoder:(NSCoder *)encoder {
// 3. levelProgress = [[NSMutableArray alloc] init];
[encoder encodeBool:currentChoosenCountry forKey:#"soundOn"];
[encoder encodeObject:levelProgress forKey:#"levelProgress"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
// 4. levelProgress = [[NSMutableArray alloc] init];
soundOn = [decoder decodeBoolForKey:#"soundOn"];
levelProgress = [decoder decodeObjectForKey:#"levelProgress"];
}
return self;
}
#end
Solution:
*I just added av init method...*
-(id)init {
self = [super init];
if (self != nil) {
levelProgress = [[NSMutableArray alloc] init];
}
return self;
}
Try this (this code may contain syntax or logic errors - I wrote it in notepad):
#interface GameState : NSObject <NSCoding>
#property (nonatomic, readwrite) BOOL soundOn;
#property (nonatomic, retain) NSMutableArray *levelProgress;
+ (GameState *)sharedState;
- (void)writeDataToCache;
#end
//
#implementation GameState
#synthesize soundOn, levelProgress;
#pragma mark - Singleton
static GameState *sharedState = nil;
+ (void)initialize {
static BOOL initialized = NO;
if (!initialized) {
initialized = YES;
if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:#"%#/gameState", pathCache]]) {
NSData *encodedObject = [[NSData alloc] initWithContentsOfFile:[NSString stringWithFormat:#"%#/gameState", pathCache]];
data = [NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
}
else
data = [[GameState alloc] init];
}
}
+ (GameState *)sharedState {
return sharedState;
}
#pragma mark - Initialization
- (id)init {
if (self = [super init]) { // will be inited while application first run
soundOn = NO;
levelProgress = [[NSMutableArray alloc] init];
return self;
}
return nil;
}
#pragma mark - Coding Implementation
- (void)writeDataToCache { // use this method to save current state to cache
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:self];
if([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache]])
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache] error:nil];
[[NSFileManager defaultManager] createFileAtPath:[NSString stringWithFormat:#"%#/GameState", pathCache] contents:encodedObject attributes:nil];
NSLog(#"GameState was saved successfully.");
}
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeBool:self.soundOn forKey:#"soundOn"];
[encoder encodeObject:self.levelProgress forKey:#"levelProgress"];
}
- (id)initWithCoder:(NSCoder*)decoder {
if ((self = [super init])) {
self.soundOn = [decoder decodeBoolForKey:#"soundOn"];
self.levelProgress = [decoder decodeObjectForKey:#"levelProgress"];
NSLog(#"GameState was inited successfully");
return self;
}
return nil;
}
#end
1 & 2 are the same (and should not work, since levelProgress is an instance variable), 3 and 4 should be encoding/decoding the array, not initializing it anew.
1 and 2 are a mistake - you can't access instance variable without specifying an instance.
You can fix 1 by changing the line to sharedInstance.levelProgress = ....
2 is just a bad idea, you're breaking common convention of initializing using init... methods, it may surprise and cause problems for an other programmer who'll be working on the same code later.
3 and 4 are ok but if loadData fails they will not be executed and the object will be initialized with ordinary init and the array will be nil pointer:
[[self alloc] init];
you should also override init and initialize the properties there.