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
Related
I am trying to wrap my head around how does the an event callback delegation works. So far I have written following code which btw works just fine:
Bridge.h
#protocol BridgeDelegate <NSObject>
- (void) bridgeLock;
#end
#interface Bridge : NSObject
+(instancetype) sharedInstance;
#property (weak, nonatomic) id<BridgeDelegate> bridgeDelegate;
- (void) wipe;
#end
Bridge.m
#implementation Bridge
+(instancetype) sharedInstance {
static dispatch_once_t pred;
static id shared = nil;
dispatch_once(&pred, ^{
shared = [[super alloc] initUniqueInstance];
});
return shared;
}
-(instancetype) initUniqueInstance {
return [super init];
}
- (void) wipe
{
NSLog(#"lock in bridge called");
if(self.bridgeDelegate)
{
[self.bridgeDelegate bridgeLock];
}
}
#end
Plugin.h
#interface Plugin : NSObject<BridgeDelegate>
#property (strong, nonatomic) Bridge *bridge;
- (void) pluginInitialize;
#end
Plugin.m
#implementation Plugin
- (void) pluginInitialize
{
self.bridge = [Bridge sharedInstance];
self.bridge.bridgeDelegate = self;
}
- (void)bridgeLock
{
NSLog(#"lock in plugin called");
}
#end
When I call the following code in applicationDidBecomeActive
Bridge* bridge = [Bridge sharedInstance];
Plugin* plugin = [[Plugin alloc] init];
[plugin pluginInitialize];
[bridge wipe];
I get the following expected output:
lock in bridge called
lock in plugin called
Now my questions:
How exactly is the delegate work? In the sense, Plugin is only implementing the function bridgewipe(), right? Why and how bridgeLock is being called at first place?
Does this have anything to the fact that Bridge is a singleton. Had I made Bridge a non singleton class, will the end result be same.
1.How exactly is the delegate work? In the sense, Plugin is only implementing the function bridgewipe(), right? Why and how bridgeLock is being called at first place?
In the above pasted code "Plugin.m" is implementing - (void)bridgeLock
Does this have anything to the fact that Bridge is a singleton. Had I made Bridge a non singleton class, will the end result be same.
No
Bridge* bridge = [Bridge sharedInstance];
this bridge we call it B1;
B1 is a instance make by the method "sharedInstance";
then you call the following code :
Plugin* plugin = [[Plugin alloc] init];
[plugin pluginInitialize];
pluginInitialize method your code is
{
self.bridge = [Bridge sharedInstance];
self.bridge.bridgeDelegate = self;
}
when code executed, self.bridge is also a instance make by the method "sharedInstance"; it's equal to B1 with address and also make B1's delegate == self;
so when you call [bridge wipe];
It will nslog #"lock in bridge called";
Because self.bridgeDelegate is not nil, so delegate will call the bridgeLock method;
Then nslog #"lock in plugin called".
About your second question, when you make Bridge a non singleton class, I think the result will be different.
#hariszaman, explanation is right but I would like to expand more on this so it can help someone in the future. Basically this is what happening.
I am creating an instance of the Bridge class. This instance in the memory has a reference variable of type BridgeDelegate.
As I instancetiate Plugin, BridgeDelegate variable starts pointing to the the Plugin class instance.
Now when lock is called, it calls the bridgelock method of the class that is pointed by BridgeDelegate pointer which in this case is Plugin.
It doesn't matter if the Bridge class is not singleton because following line in pluginInitialize:
{
self.bridge = [Bridge sharedInstance];
self.bridge.bridgeDelegate = self;
}
will be changed to :
{
self.bridge = [[Bridge alloc] init];
self.bridge.bridgeDelegate = self;
}
and steps 1,2 and 3 will be repeated the same way.
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;
I have the following situation, i can't resolve:
#interface Deck : NSObject
#interface MasterDeck : Deck
#interface PlayerDeck : Deck
Inside MasterDeck class, as part of initialization, i call
[self cutDeckImageIntoCards]; // We don't get to execute this method
Call results in an error [PlayerDeck cutDeckImageIntoCards]: unrecognized selector sent to instance
Indeed, PlayerDeck does not have this method .. but why is it being called at all?
After looking at MasterDeck's initialization i added a few debugging statements:
static MasterDeck *gInstance = NULL;
+(MasterDeck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
// MasterDeck
self = [super init];
// PlayerDeck
if (self) {
// Lots of stuff
[self cutDeckImageIntoCards]
// Some more stuff
}
gInstance = self;
return gInstance;
}
Ok, so MasterDeck is PlayerDeck because' Deck thinks it is a PlayerDeck ... Deck confirms
Deck is created as follows:
static Deck *gInstance = NULL;
+(Deck *) instance {
#synchronized(self) {
if (gInstance == NULL) {
gInstance = [[self alloc] init];
}
}
return gInstance;
}
-(id) init {
if (gInstance != NULL) {
return gInstance;
}
self = [super init];
if (self) {
// Do something
}
NSLog(#"Deck thinks it's a %#", [[self class ]description]); // PlayerDeck
gInstance = self;
return gInstance;
}
So, again
#interface Deck : NSObject
Assuming above Singleton Implementation, why would Deck think it's actually a PlayerDeck?
So the way you've written this, if you create the PlayDeck instance first, then the Deck instance is now a PlayDeck.
And then if you go to create the MasterDeck instance, your call to [super init] dutifully returns that previous PlayDeck instance.
So why is Deck a singleton at all? Deck has two subclasses that are singletons, but are you really looking for a singleton Deck also?
At a minimum, you can make this sort of work by not setting gInstance from within each init. Let the class method do that. Just return self from each of the init's. Also, remove the check for gInstance being not null, other Deck's init will always return Deck's instance once you have an instance of Deck.
But beyond that, I would rethink this idea a bit. Hope that helps.
You'll probably want to separate your singleton class from the actual class.
Try implementing it as in this example,
+(id) instance {
static dispatch_once_t pred;
static MasterDeck *sharedInstance = nil;
dispatch_once(&pred, ^{
sharedInstance = [[MasterDeck alloc] init];
});
return sharedInstance;
}
What happens if you replace [[self alloc] init] with [[MasterDeck alloc] init]?
It may be that somehow self is PlayerDeck. To make sure, you could NSLog([self description]) just before calling + alloc.
Edit
I assume that the interesting part of the code you have above is part of the #implementation of MasterDeck. My suggestion would be to try a lot more logging, including determining what super and [self class] are before calling [super init], although these may be misleading...
Also, as a side note, I believe that you should call [self release] in init if you are returning the previously-created instance.
What does the [super init] method look like? Can you step into it, or is it the default initializer?
Edit 2
I think you're doing singletons wrong. If you initialize a PlayerDeck, that would create a singleton in Deck which is an instance of PlayerDeck. Then later, when you initialize a MasterDeck, calling [super init] will return the instance already created by the PlayerDeck.
It looks like you try to be clever, but fact is - often the computer is even smarter. :)
Your deck class caches an instance in gInstance - in fact, it looks like it may store a Deck, a PlayerDeck, or a MasterDeck, depending on what and how you call / instantiate first. After that, this very instance is returned by that init method.
I strongly suggest to get this code clean and readable. I bet there are numerous problems with this code - but your problem is already a good example. Your logic (which should be simple, I guess) can surely be implemented much easier.
Note - I'm not against singletons, but this sort of code stacking is an absolute no-go. It's hard to get more dependency logic into those lines. ;)
I have a problem with the init() method of a standard NSObject. I wrote a class (EFAPersistence) which is a subclass of NSObject. EFAPersistance has a attribute called efaDatabase.
EFAPersistence.h
#interface EFAPersistence : NSObject {
FMDatabase * efaDatabase;
}
#property (assign) FMDatabase * efaDatabase;
Everytime an instance of EFAPersistance is created I want to assign efaDatabase a value from my AppDelegate.
EFAPersistence.m
#implementation EFAPersistence
#synthesize efaDatabase;
- (id)init {
if (self = [super init]) {
efaDatabase = [[NSApp delegate] efaDatabase];
}
return self;
}
#end
This way of assigning does not work. But it works if I put the code in a normal method. So I am sure that efaDatabase is correctly instantiated in my AppDelegate. It's just not working in my init() method. That's why I have the feeling that NSApp is not working inside the init() method.
That's how the important AppDelegate code looks like.
AppDelegate.h
#interface AppDelegate : NSObject <NSApplicationDelegate> {
FMDatabase * efaDatabase;
}
AppDelegate.m
- (id)init {
if (self = [super init]) {
NSString * databasePath =
[[NSBundle mainBundle] pathForResource:#"efa" ofType:#"sqlite"];
self.efaDatabase = [FMDatabase databaseWithPath:databasePath];
if (![efaDatabase open]) {
NSLog(#"Couldn't open database: %#", databasePath);
// TODO: Create a database here
}
self.db = [[EFAPersistence alloc] init];
}
return self;
}
As you can see I am calling the init method. I also affirmed this by using NSLog(). init() is called. The attribute I am trying to assign in EFAPersistence is also created before init() is called.
To sum everything up:
How can I make this work within the init() method so I do not have to write boiler plate code in all my methods of EFAPersistence?
It looks to me that your AppDelegate is unset when you try to create the EFAPersistance object the first time. This is on below line in AppDelegate.m
self.db = [[EFAPersistence alloc] init];
I imagine the app delegate is set after the init is done (returned).
This way of assigning does not work. But it works if I put the code in a normal method. So I am sure that efaDatabase is correctly instantiated in my AppDelegate. It's just not working in my init() method. That's why I have the feeling that NSApp is not working inside the init() method.
NSApp works fine.
Quoting epatel:
I imagine the app delegate is set after the init is done (returned).
Correct. The nib loader completely instantiates each object (including the app delegate, if it's in a nib), then sets it as the value of any properties it's connected to. These are two separate operations; it will not set a not-yet-initialized object as the application delegate.
Quoting you (Jens) again:
The question is how to assign efaDatabase in EFAPersistences only once . There are other methods like awakeFromNib and viewDidLoad etc. But those are not available in a plain NSObject subclass.
Incorrect. awakeFromNib is sent to every object in a nib after the object has been initialized.
That said, I'm curious as to why you have EFAPersistence in a nib. From its name, it doesn't sound interface-related. Shouldn't the app delegate own the EFAPersistence, and the EFAPersistence own the database directly?
I have some class initialized in Appdelegate, but when I get this class instance form Appdelegate in another class it has "fresh" state.
I have following in AppDelegate:
Interface:
#property (nonatomic, retain) DataController *dataController;
Implementation:
#synthesize dataController;
- (id)init {
if (self = [super init]) {
DataController *controller = [[DataController alloc] init];
self.dataController = controller;
[controller release];
NSLog(#"items: %d",[self.dataController numberOfItems]);
}
return self;
}
At this point DataControlelr class loads objects form database. Log output show "items: 10".
I have TableViewController where I need to use DataController.
TableViewController header:
#interface TableViewController : UITableViewController {
DataController *dataController;
}
#property (retain) DataController *dataController;
#end
Implementation:
-(id)init{
if (self =[super init]) {
DataController *dc =[(AppDelegate *)[[UIApplication sharedApplication] delegate] dataController];
[dc retain];
dataController = dc;
NSLog(#"items: %d",[self.dataController numberOfItems]);
}
return self;
}
Here it always says that DataController has 0 items. "fresh" state.
The Log output is always
items: 10
items: 0
It seems like assigning that class creates reference to freshly initialised DataController somehow?
How do I reference another class properly?
Thanks.
The first thing to check would be to ensure that the dc variable in the second class isn't nil-- that would cause any method called on it to 'return' 0.
It might also be useful to print out the address of the app delegate from both of those methods-- just in case the -init method is resulting from an incorrectly-allocated second instance of that class somewhere, while the regular version hasn't been initialized in the same way (or was using -initWithCoder:, etc.)
One useful rule of thumb for initialization of objects created or assigned within a nib file is to use -awakeFromNib to perform most of your initialization tasks. A corollary to this is that the app delegate can set up its state in response to the -applicationDidFinishLaunching: method. In this case, if there is a second instance of your AppDelegate class being allocated somewhere, only the one which is really set as the app's delegate will receive -applicationDidFinishLaunching:.
At the end of the day, stepping through in the debugger and looking at the call stack should show you if something isn't happening in quite the way it should.
Could there be an issue with your assignment of dataController = dc in TableViewController? In your log statement you use self.dataController, should your assignment directly above it be self.dataController = dc ?
I found the gotcha. Tanks to Jim!
Moved assignment from -init to -awakefromnib and now DataController is valid.
My mistake is that after putting the code initially in -viewDidLoad and -viewWillAppear which was wrong I thought that in -init is the place for the assignment.