Objects In Objective-C Singleton becomes null - objective-c

First of all I want to say that I am very new at both Objective-C and posting here on stackoverflow.
I am using a singleton in a tab bar application which also reads data from an API located on a web server.
My problem is, that when I first instantiate my singleton it works, but as soon as I change view by clicking in the tab bar something weird is happening. My User object which I have in my singleton is still there, but all its properties are set to null.
My singleton is implemented in the following way:
#implementation cacheobject
#synthesize location;
#synthesize currentUser;
#synthesize login;
#synthesize email2;
#synthesize isExistingUser;
+ (id)sharedCacheobject {
static cacheobject *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
self.location = 1;
}
return self;
}
- (void)dealloc {
NSLog(#"DEALLOC!!!!!!!!");
}
#end
I am using the singleton in the following way:
cacheobject *co = [cacheobject sharedCacheobject];
NSString *first = co.currentUser.firstname;
firstname2.text = first;
Whatever view is the first view in my tab bar the singleton works correctly, but as soon as I navigate to another view, my properties are null. Everything is null except for the location property.
I have logged the user object, and in both views the object comes out as:
<User: 0x8da1640>
And since the hex value is the same my guess is, that it is the same object, but the properties are just null??
Please help, I have spent two days trying to figure this out.
Kind regards.

I found the answer.
As KerrM suggested the properties in my user class was not declared correctly.
They should be strong and retain but they were weak and nonatomic.
Thanks for the help.

You're resetting the sharedCacheobject each time you call [cacheobject sharedCacheobject] but you're only initializing it once. So, the first time you're first setting the object to nil and then initializing it in your dispatch_once block. Every time after that you're setting the sharedCacheobject to nil but since the onceToken has been used it's never getting reinitialized.
If you call it twice in the same view it should still be returning nil, it just so happens that the second time you call sharedCacheobject is in another view.
Try changing
static cacheobject *sharedMyManager = nil;
to
static cacheobject *sharedMyManager;

Related

One NSDictionary visible everywhere in application

Now I am developing an iOS application which works like this:
User scans QR code,
App searches for a specific key - > value,
it gives out a value to the user.
Currently I have two ViewControllers - the main and "value" ViewController, which is inherited from main. The problem is that if I create NSDictionary in main VC it is not visible in "value" VC. Main VC gives only the string (QR code, the key) through the segue. So, the value VC has to search for key and display the value.
What I ask is some kind of global variable or one DataSource visible across the whole app. Of course, I can implement NSDictionary initialisation inside value ViewDidLoad method and it will work, but this is not the point. New modules are to be added there and the variable has to be global. I googled a lot and got the idea that singleton pattern can be helpful here. I tried to implement it, but no idea how to do. Do I need it, or it is too complex for this kind of DataSource?
Thank you!
The basic idea is, you will still need to #include the header file of the place where this dictionary will be. The solution that Naveen proposes means that you will be including the header for the app delegate wherever you want to access it. Whether to use the app delegate for this purpose or not is kinda grayish. Some people often do this, some say its a bad use of it.
The singleton approach means that you will create a class, that will always contain the same information since the init method will return object that was previously created.
For the singleton aproach, imagine I have a database manager class. So in the header of this class (the DatabaseManagerSingleton.h) ill have this:
#interface DatabaseManager : NSObject
+ (DatabaseManager*)sharedInstance;
// Your dictionary
#property (nonatomic,strong) NSMutableDictionary* someDictionary;
The implementation will look like this: (check how "sharedInstance" initializes the object)
#implementation DatabaseManager
#pragma mark - Singleton Methods
+ (DatabaseManager*)sharedInstance {
static DatabaseManager *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)init
{
self = [super init];
if (self != nil)
{
// Custom initialization
_someDictionary = [[NSMutableDictionary alloc] init];
}
return self;
}
Now, a VERY important thing is that, any place you want to use this object should first include the header:
EDIT: To use it in your code:
1) add the header
#import "DatabaseManager.h"
2) initialize the object
DatabaseManager *databaseManager = [DatabaseManager sharedInstance];
3) do whatever you need
// Initialize the dictionary
databaseManager.someDictionary = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"OBJECT",#"someKey", nil]; // In this case the object is just a NSString.
// Access
[databaseManager.someDictionary objectForKey:#"someKey"];
Put as a property on Appdelegate
#property (nonatomic,strong) NSDictionary * sharedData;
Access anywhere like
NSDictionary *sharedData= ((APPDelegate *) [UIApplication sharedApplication].delegate).sharedData;

Updating view with a data from the method of another class

I am trying to update my UIProgressView with some data from a method of my utility class.
Now, just because for updating my UIProgressView, i am holding that method in my view controller class and everything works fine. Because i can reach the loop in that method with a global variable so i can update my progress. But if i want to move this method to my utility class, what am i supposed to do to keep informed my UIProgressView. Thanks.
What I would suggest is to redesign your utility class to be a singleton
Here is an example of code of your utility class:
UtilityClass.h file:
#interface UtilityClass : NSObject
+ (UtilityClass *)sharedInstance;
- (CGFloat)awesomeMehod;
#end
UtilityClass.m
#implementation UtilityClass
+ (id)sharedInstance
{
static UtilityClass *_instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[UtilityClass alloc] init];
});
return _instance;
}
- (id)init
{
self = [super init];
if (!self) return nil;
// Regular initialization, however keep in mind that it will be executed just once
return self;
}
- (CGFloat)awesomeMethod
{
return 42.0f
}
#end
Now from your view controller you will call
CGFloat progress = [[UtilityClass sharedInstance] awesomeMethod];
[self.progressView setProgress:progress];
keep in mind several things:
It's one of possible approaches and I would go and read about various
design patterns that might come in handy one day
Probably a good idea to refresh knowledge on view controllers and the way they interact
For class to become a proper singleton, you also should override
methods such as alloc, init, initWithZone, dealloc, release
etc (list of methods to override will vary if you use ARC), here is
an example of doing that, although dispatch_once takes care of
#synchronize() call. For now, as long as you "instantiate" you class only
through calling sharedInstance class method you will be fine.

Property not set in drawRect method - iOS

I have been seeing some strange behavior when I try to access a class variable or a property in my drawRect method..
In my .h file I have the following
#interface DartBoard : UIView
{
Board * board;
int index;
}
#property (readwrite, assign, nonatomic) NSNumber * selectedIndex;
#end
In my .m file I have the following
#implementation DartBoard
#synthesize selectedIndex;
-(id)init
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"selectedIndex: %d",[self.selectedIndex intValue]);
NSLog(#"index: %d",index);
}
#end
the output is
2012-06-12 19:48:42.579 App [3690:707] selectedIndex: 0
2012-06-12 19:48:42.580 App [3690:707] index: 0
I have been trying to find a solution but have had no luck..
I found a similar question but there was no real answer to the issue
See: UIView drawRect; class variables out of scope
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly but how do I fix it?
Cheers
Damien
I have a feeling drawRect is different that normal methods and is not getting the scope of the class correctly
No, there is nothing special about -drawRect:.
There are two possibilities:
1. Your -init method is not being called.
You didn't say how this view gets created -- if you are manually calling [[DartBoard alloc] init], or if it is getting unarchived from a nib file.
If it's coming from a nib, UIView's unarchiving doesn't know that your init method should be called. It will call the designated initializer instead, which is -initWithFrame:.
So, you should implement that method instead, and make sure to call super!
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.selectedIndex = [NSNumber numberWithInt:5];
index = 123;
}
return self;
}
2. There might be two instances of your view: one that you are manually initing, and another one that comes from somewhere else, probably a nib. The second instance is the one that is being drawn. Since its variables and properties are never set, they show up as zero (the default value).
You could add this line to both your -init and -drawRect: methods, to see what the value of self is. (Or, check it using the debugger.)
NSLog(#"self is %p", self);

Weird memory issues, with ARC enabled

I am having a very, very strange error, probably related to memory management (even though I'm using ARC).
I have a my AppDelegate, Foo, and SubFoo (which is a subclass of Foo).
Foo.h
#protocol FooDelegate <NSObject>
- (void)didReceiveDownloadRequest:(NSURLRequest *)downloadRequest;
#end
#interface Foo : NSObject {
__weak id <FooDelegate> delegate;
}
- (void)performRequest;
#property (nonatomic, weak) id <FooDelegate> delegate;
#property (nonatomic, retain) NSString *fileIdentifier;
Foo.m
#implementation Foo
#synthesize delegate, fileIdentifier;
- (id)init {
if ((self = [super init])) {
self.delegate = nil; // I tried leaving this line out, same result.
NSLog(#"I am %p.", self);
}
return self;
}
- (void)performRequest {
// Bah.
}
#end
SubFoo.h
#interface SubFoo : Foo {
WebView *aWebView;
}
SubFoo.m
- (void)performRequest {
if (self.fileIdentifier) {
aWebView = [[WebView alloc] init];
[aWebView setFrameLoadDelegate:self];
[[aWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"theURL"]];
}
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
NSLog(#"Finished loading.");
// ...
NSLog(#"Class Name: %#", NSStringFromClass([self class]));
NSLog(#"Memory Location of delegate: %p", self.delegate);
// ...
}
Sometimes, the class name on webView:didFinishLoadForFrame: returns a completely different class (instead of SubFoo, it returns random classes, like NSSet, NSArray, it even sometimes returns CFXPreferencesSearchListSource), other times it just crashes there with an EXC_BAD_ACCESS, and when it returns a random class on Class Name: it returns that [randomClassName delegate] is an unrecognized selector.
EDIT: When self gets set to another thing, it gets set RIGHT on webView:didFinishLoadForFrame:, and on performRequest it is ALWAYS SubFoo.
Any help here would be appreciated.
First, even though you are using ARC zeroing weak references in your project (#property (weak)), other projects and frameworks may not be (and are probably not) using zeroing weak references.
In other words, assume that all delegates in frameworks are __unsafe_unretained unless:
The delegate property is declared weak in a header
The documentation/header explicitly states otherwise
That said, let's talk about your example. Your object ownership chart looks something like this:
(Note: I'm not entirely sure which class in your project uses SubFoo. Based on common practice, I'm assuming that you have a class with a strong reference to SubFoo, and that class is also set up to be a SubFooDelegate)
Ultimately, your instance of SubFoo is losing its last strong reference and is deallocating. In a perfect ARC-enabled world, the WebView's pointer to SubFoo would nil out at this time. However, it's not a perfect world yet, and WebView's frameLoadDelegate is __unsafe_unretained. Due to run loop interaction, the WebView is outliving SubFoo. The web request completes, and a dead pointer is dereferenced.
To fix this, you need to call [aWebView setFrameLoadDelegate:nil]; in SubFoo's dealloc method. You also need to call it when you reassign aWebView, as you are losing track of the old aWebView:
SubFoo.m
#implementation SubFoo
- (void)dealloc {
[aWebView setFrameLoadDelegate:nil];
// Also nil out any other unsafe-unretained references
}
- (void)performRequest {
if (self.fileIdentifier) {
[aWebView setFrameLoadDelegate:nil]; // Protects us if performRequest is called twice. Is a no-op if aWebView is nil
aWebView = [[WebView alloc] init];
[aWebView setFrameLoadDelegate:self];
[[aWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"theURL"]];
}
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame {
// ...
}
Forget the self.delegate error for now, it is a red herring if [self class] is producing the wrong result! Your results suggest you are somehow clobbering self.
Breakpoint on webView:didFinishLoadForFrame: check the self value and step through.
Comment Followup
For self to be wrong on the first statement of an instance method is, let's say, unusual (but not impossible).
It is important when an object is set as another's delegate that you make sure the delegate object's lifetime is at least as long as the one it is acting as a delegate to. Introducing ARC can make previously working code fail as it may release the delegate earlier than the code did under MRC. When this happens the call to the delegate usually fails.
However your error does not fail on the call to the delegate; the call starts - you end up in webView:didFinishLoadForFrame: - and then you find self is invalid. To actually invoke an instance method usually requires a valid value for self as it is used to determine the method implementation to call. Hence it is usual for self to be valid at the start of a method!
But note the "usually"...
So despite you having successfully reach your method, your error might be down to not having a strong reference to your SubFoo instance, you pass it as a delegate to aWebView, and by the time webView:didFinishLoadForFrame: is called your SubFoo has gone.
Make sure you're keeping a strong ref to your SubFoo instance. If you just want to test (this is not a recommended general solution!) if this is your problem you can just assign it to a local static (static SubFoo *holdMe say declared inside performRequest) in performRequest, which will keep a strong reference around at least until the next call to performRequest. If this does prove to be the problem you then need to come up with a good way to maintain the reference that fits your design.
Here's the real problem: You're creating a SubFoo object within the context of a method. So after the method completes, SubFoo is being released (before its WebView has time to load).
To fix this, you'll need to assign the SubFoo object you're creating to something persistent, like a instance variable of the class you're creating it from. That way the object will persist beyond the scope of the method it was created in and all will work as expected.
As CRD mentioned, I would say an incorrect object/bad access returned is a sign of an object being released. Sometimes it's replaced by another object, sometimes it's not so you get the bad access exception. Regarding how this could happen to self, I would imagine that this is a concurrency weird case (object is being freed on another thread).
The best way to confirm this is to run your code in Instrument's NSZombie template, it'll show you as soon as you access a freed object. It also shows when it's been retained/released so you don't have to guess.
Regarding your above comment.
SubFoo *theClass = [[SubFoo alloc] init];
You must store theClass in a
#property (strong) SubFoo *mySubFoo;
If you declare it as such:
{
SubFoo *theClass = [[SubFoo alloc] init];
}
It gets released at the closing bracket. This part of the point of ARC when that variable moves out of scope, it gets released. If you want to let it float in the ether you could use
{
__weak SubFoo *theClass = [[SubFoo alloc] init];
}
and it won't get released, but this will lead to a memory leak unless you carefully manage all the weak references. In the case of it not being released at -performRequest I'm assuming the request looks like this:
{
SubFoo *theClass = [[SubFoo alloc] init];
[theClass performRequest];
}
wheras -webView:didFinishLoadForFrame: is called at some indiscriminate time in the future.

Singleton's property memory management

I'm trying to write my little app and experiencing some memory management problems.
At first, I have Game singleton object with property:
//Game.h
#interface Game : NSObject
#property (nonatomic, retain) MapBuildingsLayer *mapBuildingsLayer;
+(Game *) game;
-(BOOL) addObject:(NSString *) objName At:(CGPoint) pt;
#end
where MapBuildingsLayer is just cocos2d CCLayer instance
//Game.m
#implementation Game
#synthesize mapBuildingsLayer = _mapBuildingsLayer;
static Game *instance = nil;
+ (Game *)game {
#synchronized(self) {
if (instance == nil) {
instance = [[Game alloc] init];
}
}
return instance;
}
-(BOOL) addObject:(NSString *)objName At:(CGPoint)pt
{
if([objName isEqualToString:OBJ_TYPE_PIT])
{
if([[Game game].mapBuildingsLayer addPitAt:pt]) //app crashes here
{
[self toggleConstructionMode];
return YES;
}
}
return NO;
}
#end
In MapBuildingsLayer.m's init method I use Game's mapBuildingsLayer property to store a reference to this CCLayer instance in my singleton (for future use in other methods):
//MapBuildingsLayer.m
#implementation MapBuildingsLayer
-(id) init
{
if( (self=[super init])) {
[Game game].mapBuildingsLayer = self;
}
return self;
}
When I call Game's addObject:objName At: method, my app crashes with EXC_BAD_ACCESS.
How I must declare property in Game singleton to use it from other places in my app's lifetime?
You typically don't use the singleton in the class itself, try changing
if([[Game game].mapBuildingsLayer addPitAt:pt]) //app crashes here
to
if([self.mapBuildingsLayer addPitAt:pt]) //app crashes here
You should be using [Game game] external to the class to get into the singleton instance of your class and call its methods, but internal to the class you would just refer to it as self like normal.
Typically if you're going to use a game singleton this isn't how you'd use it. Try thinking of it like this with a giant state machine, Create a CCScene subclass which will initialize all of your respective CCLayer subclasses and control them. Then from the statemachine you can load the appropriate initializing its scene and that will create everything under it.
In your applicationDidFinishLaunching method you simply have your singleton object load your first scene. I'd really recommend checking out the Learning Cocos2d Book as it covers this singleton state engine very well and I think would clear up all your questions.
Bottom line is have the state engine load the scene which loads the layers.
No where in your code i am seeing your mapBuildingsLayer initialized. I hope before returning your instance you should also do
instance.mapBuildingsLayer = [CCLayer alloc] init];
I think the way you are assigning the mapBuildingsLayer is wrong. Remove [Game game].mapBuildingsLayer = self from your MapBuildingsLayer init method and instead add the following inside Game init method:
self.mapBuildingsLayer = [[MapBuildingsLayer alloc] init] autorelease];
now it is initialized inside your singleton init method so you can access it simply as [Game game].mapBuildingsLayer anywhere outside the Game class. If this doesnt work try posting what addPitAt: does.
hope this helps