Calling subclass methods - Cocos2d - objective-c

I'm working through the 'Learning Cocos2d' book, and I'm stuck on something basic.
So, theres a parent Class: GameplayLayer. In it, there's an 'init' method which creates an instance of the main character in here - 'Viking'. Viking is a subclass of 'GameCharacter', which is a subclass of 'GameObject'.
#pragma mark INIT PLAYER
Viking *viking = [[Viking alloc]
initWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"viking.png"]];
[viking setJoystick:leftJoystick];
[viking setFireButton:fireButton];
[viking setSecondaryButton:secondaryButton];
[viking setCollisionLayer:collidableLayer]; // send collision layer to player
[viking setPosition:ccp(screenSize.width * 0.35f, screenSize.height * 0.14f)];
[viking setHealth:100];
[sceneSpriteBatchNode addChild:viking z:1000 tag:kPlayerSpriteTagValue];
Now, Viking has an update method which is called every frame by GameplayLayer. It's parent class, GameObject also has this update method, which brings up an error message if it is accidentally called - "GameObject update should be overridden'.
So in my code, I'm calling the update method of 'Viking' with the following method:
#pragma mark UPDATE_METHOD
-(void) update:(ccTime)deltaTime
{
CCArray *listOfGameObjects =
[sceneSpriteBatchNode children];
for (GameObject *tempChar in listOfGameObjects) {
CCLOG(#"engine found %#",tempChar);
[tempChar updateStateWithDeltaTime:deltaTime
andListOfGameObjects:listOfGameObjects];
}
}
So, this is supposed to call the 'updateStateWithDeltaTime' method in Viking. But somehow, it's calling the parent method in GameObject which says 'updatestate should be overridden'. How do I override the parent method?
Many thanks,
Carl

You need to cast tempChar to a Viking.
for (GameObject *tempChar in listOfGameObjects)
{
[(Viking *) tempChar updateStateWithDeltaTime:deltaTime
andListOfGameObjects:listOfGameObjects];
}
Because you're doing a for loop with fast enumeration of GameObjects, the local variable assumes that all objects are GameObjects. You need to explicitly cast tempChar to a Viking so that the program knows which class to look for the method in.
As an interesting side note, if GameObject didn't have the same method as Viking did, you would be getting a warning in XCode telling you it couldn't find the method you're asking for (because it needs to know that every object this could be called on has it).
You might want to check that the object you're calling this on is the correct class (if you only want to call this on Viking objects). You'd add if ([GameObject isKindOfClass[Viking class]) above your update method call.

Related

Delegate gets called but the calling viewcontroller is released

Probably a noob question, but I cannot seem to get it right at the moment. I am working on an app where I have an Actionsheet for the confirmation of some basic things. However after the delegate is called for that Actionsheet my initial calling object is released (or not initiated).
In the delegate method I then want to call a method on that object but it just not do anything from that point.
The self.inviteSponsorsFromVC is not initiated anymore in this scenario and I want to call the saveSponsorWithEmail method from it. I cannot just reinitiate it, as the object had some objects in it, it has to use.
Everything works correctly if I just remove the actionsheet and call the saveSponsorWithEmail method directly without using a delegate.
This is my delegate method:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
//Get the name of the current pressed button
NSString *buttonTitle = [actionSheet buttonTitleAtIndex:buttonIndex];
if ([buttonTitle isEqualToString:NSLocalizedString(#"Send invitation", nil)]) {
ContactFromAddressBook *contactFromAddressBook = [self.tableData objectAtIndex:selectedIndex.row];
[self.inviteSponsorsFromVC saveSponsorWithEmail:contactFromAddressBook.email andName:contactFromAddressBook.fullName];
}
if ([buttonTitle isEqualToString:NSLocalizedString(#"Cancel", nil)]) {
NSLog(#"Cancel pressed --> Cancel ActionSheet");
}
}
My guess is that at in the delegate method the content of self.inviteSponsorsFromVC is nil. In Objective-C, when you send a message to nil the message is simply ignored (unlike C++, for instance, where you get a crash when you call a method on a NULL object).
As an experiment you can try either one of these:
If you use ARC, make the property self.inviteSponsorsFromVC a strong reference
If you don't use ARC, say [self.inviteSponsorsFromVC retain] at some point before you display the action sheet
Either way, what you need to do is to make sure that the object in self.inviteSponsorsFromVC is not deallocated before you invoke a method in it.
EDIT after your comment
The property declaration is good, it's got the strong attribute on it. In your InviteSponsorsFrom class, try to add a dealloc method and set a breakpoint there to see if the object is deallocated, and where the call comes from.
- (void) dealloc
{
[super dealloc];
}
Also make sure that an instance of InviteSponsorsFrom is created in the first place. I assume you have an initializer somewhere in that class where you can set a breakpoint and/or add an NSLog statement to make sure that the instance is created.

Objective-C - object is init'd using its supers custom init, how can you call your own init logic?

I'm going through a cocos2D book and I'm trying to get a Radar Dish object initialized. The animations are set up in the initAnimations method (which is called in the init method), however the init method is never called. In the GameLayer.m they use a createObjectOfType method which calls:
RadarDish *radarDish = [[RadarDish alloc] initWithSpriteFrameName:#"radar_1.png"];
So initWithSpriteFrameName is an init of RadarDish's super class, and it sets up the Radar Dish. So when does RadarDish.m's init ever get called?? It's causing my program to crash because the animations never get set up in the init.
You need to override initWithSpriteFrameName: in any subclass, including RadarDish, which needs its own initialization steps. In that method, you need to call up to the superclass's designated initializer and then continue with your subclass's particular needs, like so:
- (id)initWithSpritFrameName: (NSString *)frameName
{
self = [super initWithSpriteFrameName:frameName];
if( !self ) return nil;
[self initAnimations]; // As long as initAnimations doesn't also call a
// superclass's initializer!
// Other setup...
return self;
}
Now [[RadarDish alloc] initWithSpriteFrameName:...] will use this implementation, ensuring the animations are set for your object.
You also should change the name of initAnimations, because Cocoa convention is that only actual instance initializers -- methods which take a "raw" instance and prepare the instance fully -- should begin with init. Call it setUpAnimations or something like that.

iOS MVC - How to pass data from model to controller?

I did quite a bit of research on this, but I am having a mental block about my problem. I am working on Objective-C for an iOS app
Here's my set up:
The view controller gets a text from the view (user input), and passes that text to the MethodA of the model.
The MethodA in model works on the input text and gets an output (e.g. searches google for that text). It does the search using dispatch_async method which calls the selector to MethodB within model.
MethodB parses the output and puts all the results into a NSMutableArray
My Question Here: how do I pass that NSMutableArray back to view controller so I can show it on the view?
Sorry, if the answer to my question is very simple/obvious. I am new to Objective-C
Any time I want to do async processing and that stuff needs to get back into the UI somewhere, I do one of two things:
1. Use NSNotification to tell anyone who cares that the work is complete
2. Use a delegate property on the worker and a #protocol
1 NSNotification
The model object should document in it's .h file that it fires notifications when certain things happen; such as when a portion of the model has been updated. When the ViewController initializes the model object, have it set itself up as an observer of the documented notification, and implement a callback which updates the UI.
2 delegation and #protocol
Create a #protocol such as #protocol FooModelUpdateDelegate with a method properly named such as fooObjectDidUpdate:(Foo *)object;, and then the model class has a delegate property as id<FooModelUpdateDelegate> updateDelegate and the ViewController sets itself as that delegate, and I'm sure you can figure out the rest.
I guess passing along a delegate-object that respoons to a selector-method and calling this method with the processed data will be a good way to achieve the loosley coupled structure your program deserves. Are you familiar with this concept, or shall I dig up some code-samples for you?
UPDATE: Code samples
So, I would probably use the calling class, say MyViewController, to implement the callbackMethod, myCallbackMethod as follows:
-(void) myCallbakcMethod: NSMutableArray* array {
//DoWhatever with the returned data
}
The point is to get the result passed back to this method when the computation is finished.
So in your MyViewController where you call MethodA you pass along a reference to the delegate to handle the result, namly self.
//From this:
[MyModel methodA:param1]
//To:
[MyModel methodA:param1:self]
MyModels methodA and methodB would need to add a parameter (id)delegate and pass that along between the calls.
In methodB where the data myArray is ready, do the following call:
if([delegate respondsToSelector:#selector(myCallbackMethod:)]])
[observer performSelector:#selector(myCallbackMethod:) withObject:myArray];
In your view controller:
// your controller code
...
[myModel getSearchResult:searchKeyword receiver:self action:#selector(responseReceived:)];
}
- (void)responseReceived:(MyModel *)model
{
NSArray *searchResult = model.searchResult;
[model release], model = nil;
// some view actions, for instance update your table view
...
}
In your model:
...
- (id)getSearchResult:(NSString *)searchKeyword receiver:(id)aReceiver action:(SEL)receiverAction {
self.receiver = aReceiver;
self.action = receiverAction;
// some async actions
}
In async response receiver:
...
[self.receiver performSelector:self.action withObject:self];
}
Unless I'm misunderstanding your description it sounds like your "model" class is doing more than it should. In this case it's doing at least some of the work of your controller. My suggestion would be to fold methodA and methodB into the view controller (or another controller class). Method B could still set the NSMutableArray property of "model" instance, if that's essential (or skip that step if it's not).
-(void)methodA {
NSMutableArray *someArray = ...
[self methodB:someArray];
}
-(void)methodB:(NSMutableArray*)array {
NSLog(#"%#", array);
// do something with the array, like update the view
}
But if both are methods inside the view controller why not just update the view inside the method instead of passing it to another method?

Objective C custom class methods not being called

So I have this custom class with just a test method that does nslog. I am going to reuse this method many times in my app. The interface looks like this.
#interface TestViewController: UIViewController { CMImageMover * imageMover }
Then in the view did load I:
imageMover = [[CmImageMover alloc] init];
If I do:
[imageMover testMethod];
Right after the alloc and init it works in the viewDidLoad function but if I call it again from another function in the view controller nothing works and the class method does not get called.
What am I doing wrong here. Every other var I declare like NSArray/NSTimer, I do the say way and I am able to access and use it throughout my controller.
When you say "if I call it again from another function in the view controller nothing works" then first thing to check is what you are sending the testMethod. It could be nil, in which case nothing will happen. In objective C sending a message to nil does nothing. Add an NSLog to find out, e.g.
NSLog(#"imageMover object is: %#", imageOver);
[imageMover testMethod];
If the NSLog shows it is nil - or something crazy - then follow up what you are doing with the imageMover ivar.
You mention a class method in your question, but don't refer to it in your code snippets.
If you have defined testMethod as a class method it will, of course, fail if you send that message to an instance. (And it will fail noisily.) A class method would be introduced like this:
+ (void) testMethod
{
NSLog(#"CMImageMover testMethod called on Class");
}
An instance method would be introduced like this:
- (void) testMethod
{
NSLog(#"testMethod called on an instance of CMImageMover");
}
Apologies if this is all screamingly obvious to you and missing the point of the question. It's not that clear from your question where the issue lies.

Selectors in Cocos2d schedule method

So I am doing this to initialize my selector:
//In .h
SEL selectors[3];
//In .m
selectors[0] = #selector(rotate);
selectors[1] = #selector(discharge);
And here is the problem:
When I call this in my init method in Cocos2d like this:
[self performSelector:selectors[0]];
it works fine, but when I call this line of code in a method called moveThings which is invoked through the schedule ([self schedule:#selector(moveThings:)]) at the end of my init method in Cocos2d it gives EXC_BAD_ACCESS. What is the problem with scheduling things?
UPDATE:
I have found there is a problem with the rotate function (the function being stored in selector[0]). Here it is:
-(void)rotate:(ccTime)delta {
if (((CCSprite *)[creature objectAtIndex:0]).rotation < 360) {
((CCSprite *)[creature objectAtIndex:0]).rotation++;
}
else {
((CCSprite *)[creature objectAtIndex:0]).rotation++;
}
}
If I comment the contents of the method out it works fine when called through moveThings and init.
If I change the methods contents with:
((CCSprite *)[creature objectAtIndex:0]).rotation++;
It fails... But, again, I would like to state that all of these things do work if I call it in my init method, even call it twice in a row, but it will not work (except when I take out the contents of the rotate method) if I call it through the moveThings: method which is being invoke through the schedule method it fails.
Further update:
If I call:
((CCSprite *)[creature objectAtIndex:0]).rotation++;
In moveThings (which is being, as I've said before, invoked by the schedule:(SEL) method) it fails. Where as long as it is not invoked through a method that is the called by schedule it works.
The problem is that when you call performSelector there are only two options:
have your selector take no arguments and leave the ":" off the #selector(foo) definition.
have your selector take either one or two arguments which both must be an NSObject or subclass
it is the latter that is messing you up here I suspect.
Here are the three forms of performSelector:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
You'll note that the withObject arguments are all of type id which is an Objective C object.
The selector you're trying to use takes a ccTime which is a float and not an Objective C object as it's param and that is why things are crashing:
-(void)rotate:(ccTime)delta;
One option is to make a wrapper method that takes a wrapped ccTime and unwraps it and calls the rotate:(ccTime) method:
- (void) wrappedRotate: (NSNumber*) inDelta
{
[self rotate: [inDelta floatValue]];
}
then use
selectors[0] = #selector(wrappedRotate:);
and then call via:
[self schedule: #selector(moveThings:)]); // cocos2d schedule
...
- (void) moveThings: (ccTime) dt
{
[self performSelector: selectors[0] withObject: [NSNumber numberWithFloat: dt]];
...
}
One reason you are getting confused is because Cocos2d is using #selector in somewhat more complicated ways (see CCScheduler.m in the CCTimer::initWithTarget:selector:interval: and CCTimer::update: method in particular).
disclaimer: code typed into SO so not checked with a compiler, but the essence of what you need should be here.
One problem for sure is that you are using a variable declared inside a .h while initializing it inside the relative .m. According to the linking I'm not sure that just one variable selectors will exist (so that different files that include .h will have different versions).
First of all I suggest you to try adding the keyword extern to have
extern SEL selectors[3];
to tell your linker that it is initialized inside the relative .m and to use just that one.
I think your problem stems from your method definition which is - (void)rotate; and not - (void)rotate:(ccTime)dt;
You should adjust your selectors likewise.
If your method does not have any arguments then do not use a colon in your selector call.
// Requires #selector(foo:)
- (void) foo:(id)sender;
// Requires #selector(foo)
- (void) foo;