in objective-c initializing an object and giving it an instance name - objective-c

in cocos2d-iphone I have a CCLayer class called GameScene
I want to use this class from another class however I dont know the name of the instance of the GameScene class
I initialized an instance of GameScene class as follows
-(id) init {
if ((self = [super init])) {
however this will not give me the name of the instance
I read in documentation that a method called initWithName can be used so I tried something like this but it does not work it gives me warning:
In function '-[GameScene init]':
warning: 'CCLayer' may not respond to '-initWithName:'
the code I tried is
-(id) init {
if ((self = [super initWithName:"gamescene"])) {
I will need only one instance of this class through the game but I cant catch a handler of that instance so I can use it from other classes?
any Idea
Many Thanks
update:
Hello
I am going to update the code to let you know that I have tried your solution but it dosnot seem to work yet
in the MyAppDelegate.h I have this lines of code:
firstly I have defined the app delegate to share it with other classes
MyAppDelegate.h
#define AD (MyAppDelegate *)[[UIApplication sharedApplication] delegate]
and in the MyAppDelegate.m I have the following code:
gs = [[GameScene alloc] init];//this is the gamescene
sc = [gs scene]; //this calls the method -(id)(scene)
[[CCDirector sharedDirector] runWithScene: sc]; //runwithscene
now when I tried to use the gs within other class -for example player.m class-
player.m
GameScene* gs = [AD gs]; //retrieving the instance from appdelegate
[gs updateScoreByAmount:5];/calling the method "updateScoreByAmount"
the results , guess what happens?
the program runs without errors however gs instance seems to be different than the one which is run by the appdelegate since this method "updateScoreByAmount" does not affect the scene which run by the runWithScene in the appdelegate
any idea?
Many Thanks to all those who have tried to help

If I am understanding you correctly, you need a reference to an instance of the GameScene class? If that is the case, it depends how the object is created. You probably are creating the instance from some controller class and it would look something like:
GameScene *gameScene = [[GameScene alloc] init];
Here, gameScene would be the reference to the instance you need.

ahmed,
Instead of storing a pointer to your top level scene, you can do this to get access to it:
GameScene* myScene = (GameScene*)[[CCDirector sharedDirector] runningScene];
CCDirector is a singleton, and it knows what scene it's currently running, so you don't need to keep a pointer to it, you can use the above way of getting to it when you need it.
Because GameScene is a child of CCScene your running scene will be your GameScene object.
Hope that helps.

Related

Setting Delegate and Lazy instantiation

I have an App with an NSWindowController and an associated XIB which is not needed most of the time (admin interface window), so I wanted to use Lazy instantiation. However, I also need to setup the 'delegate' and that is when the errors happen.
I have everything setup in the AdminWindow,h (subclass of `NSWindowController') and .m file.
In my main controller, MainController (subclass of NSObject) I have the following working code.
#interface MainController : NSObject<AdminWindowDelegate>{
AdminWindow *myAdminWindow;
}
#implementation MainController
-(id)init{
myAdminWindow = [[AdminWindow alloc] init];
[myAdminWindow setDelegate:self];
}
-(IBAction)openAdminWindow:(id)sender{
[myAdminWindow showWindow:nil];
}
So that all works, but I don't want to instantiate myAdminWindow until it's needed, thought lazy instantiation would work.
Altered the MainController:
#implementation{
-(AdminWindow *) myAdminWindow{
if(!_myAdminWindow){
_myAdminWindow = [[AdminWindow alloc] init];
//Tried to set delegate here, but does not work
}
-(IBAction)openAdminWindow:(id)sender{
[self.myAdminWindow showWindow:nil];
}
Where do I set the delegate? I tried just after 'alloc' 'init' of myAdminWindow, but it does not work. when I start typing the command
_myAdminWindow.setDe... Xcode gives nothing, .setDelegate or .delegate are not options.
I've tried
[_myAdminWindow setDelegate Nope, does not work either.
Leaving out the delegate portion, everything else works as desired.
Question: When using lazy instantiation , where do I set the delegate? and how?
Thank you in advance
===[EDIT]===
In case someone else has the same question.
Thank you to Phillip Mills for the reply and reminder.
I removed the following declaration in #interface of MainController:
AdminWindow *myAdminWindow;
And declared myAdminWindow in the MainController's interface section as a property - and all is good!

Assigning another viewcontroller at initWithNib

I am just curious if lets per say I have a singleton property of webView I am trying to assign at the initialization point of another viewController. The compiler is generating the error indicating "Incompatible pointer types".
I am not sure why it is doing so, as the Super Class of that Class is still UIViewController. Any help here would be really appreciated.
Thanks.
Code below:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nil bundle:nil];
if (self) {
// Work your initialising magic here as you normally would
if ([[CartCheckout sharedInstance] universalVC]) {
//self = [[CartCheckout sharedInstance] universalVC];
NSLog(#"testing");
self = [[CartCheckout sharedInstance] universalVC];
}
NSLog(#"initWithNibName allocated");
}
return self; }
This is happening because the view controller of the actual instance you are trying to do the assignment to is not of the correct type, it doesnt matter that they share a super class.
self in this case is of one and only one type, the type of the view controller subclass.
Also, even if they were the same type, Objective C is not going to let you replace the instance of the class in an init method with anything.
init methods are instance allocation methods, if you return a pointer to something besides the instance you are actually in, you are going to crash immediately following.
So you have two problems, one, you are trying to inject a singleton instance into an init method, and two you are trying to hijack the container of a view.
Neither of these will work. Instantiate the correct type of viewController to go with your view at the time you need it.
What you want instead is this:
MyUniversalViewController *universalVC = [MyUniversalViewController alloc] initWithNibName:#"blabla
Trying to switch types in the constructor is too late.

Where and how should I instantiate an object which will be used globally in an IOS app?

For simplicity's sake, lets say I am making a stopwatch application for ios5. I have a class called stopwatch, which represents the stopwatch's state. This class possesses members that store elapsed time, whether the stopwatch is running, etc.
To me, it seems this class should be instantiated once at the beginning of my apps lifetime, thus I declare it to be a member of the AppDelegate class and instantiate it in the didFinishLaunchingWithOptions: method of the AppDelegate class
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
Stopwatch *stopwatch = [[Stopwatch alloc] init];
return YES;
}
However, I now find myself needing to be able to reference this stopwatch object in the ViewController class but not knowing how to do so.
I would greatly appreciate any advice as to how to approach this problem, as it seems to be a fundamental concept for making ios applications. Thank you!
Don’t use a singleton. There are plenty of reasons why singletons are a bad design decision in most cases, I wrote a blog post about some of them.
Don’t store arbitrary data in the app delegate. That’s just misusing an existing singleton to do what you want. The app delegate should be concerned with the app’s lifecycle events, it should not be a place to stick whatever data you want accessible from everywhere.
One possible correct approach to solve this problem is to have a class that will create you application’s object graph for you and pass the instances to all interested classes. My app delegate’s application did finish launching method mostly look like this:
- (BOOL) applicationDidFinishWhatever
{
Factory *factory = [[Factory alloc] init];
[self setWindow:[factory buildMainWindow]];
[window makeKeyAndSomething];
return YES;
}
And all the object creation and assembly gets done in the Factory class. The Factory will create the timer (and keep the single instance in memory, if needed) and it will pass the timer to view controllers:
- (void) buildMainWindow
{
UIWindow *window = …;
[window setRootViewController:[self buildMainController]];
return window;
}
- (void) buildMainController
{
UIViewController *controller = …;
[controller setTimer:[self buildTimer]];
return controller;
}
I have created a sample Xcode project that shows how to wire an app without singletons. It’s very simple right now, I will add some common use cases later.
You can use Singleton pattern. look here for more info What should my Objective-C singleton look like?
If you "declare it to be a member of the AppDelegate class" then you should use it as a property:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
Stopwatch *aStopwatch = [[Stopwatch alloc] init];
self.stopwatch = aStopwatch;
[aStopwatch release];
...
}
Later, anywhere in your code:
appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate doSomethingWithStopwatchMethod];
someVariable = appDelegate.stopwatch.timeSinceStart;
...
Another approach would be to use a singleton (check SO for dozens of questions on the subject) or even a plain global variable (an integer holding a flag for instance) declared in main.m.

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

How do get reference to another class properly?

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.