How to pass arguments when calling function with timer in objective c - iphone

-(void)setX:(int)x andY:(int)y andObject:(Sprite*)obj
{
[obj setPosition:CGPointMake(x,y)];
}
Now, I want to call above method, using following timer.
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector() userInfo:nil repeats:NO];
What to set Here?
How to Pass arguments? (as per my knowledge - selector specifies only the method to invoke)

You'll need to used +[NSTimer scheduledTimerWithTimeInterval:invocation:repeats:] instead. By default, the selector used to fire a timer takes one parameter. If you need something other than that, you have to create an NSInvocation object, which the timer will use instead.

If you have a fairly complex set of arguments that you want to use to invoke the method, I would recommend capturing the arguments into something that holds a configuration and can do whatever it is that needs doing based on that configuration...
Something with an interface like this:
PositionSetter.h:
#interface PositionSetter : NSObject
{
NSInteger x;
NSInteger y;
Sprite *target;
}
+ positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite;
- (void) applyPosition;
#end
PositionSetter.m:
#interface PositionSetter()
#property(readwrite, nonatomic) NSInteger x;
#property(readwrite, nonatomic) NSInteger y;
#property(readwrite, nonatomic, retain) Sprite *target;
#end
#implementation PositionSetter
#synthesize x, y, target;
+ positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite;
{
PositionSetter *positionSetter = [PositionSetter new];
positionSetter.x = xPos;
positionSetter.y = yPos;
positionSetter.target = aSprite;
return [positionSetter autorelease];
}
- (void) applyPosition;
{
[self.target setPosition:CGPointMake(self.x,self.y)];
}
#end
Usage is quite straightforward:
positionSetter = [PositionSetter positionSetterWithX: 42 y: 21 sprite: mySprite];
[positionSetter performSelector: #selector(applyPosition) withObject: nil afterDelay: 1.0];
While a tad more code, the resulting implementation will be fast enough -- probably faster than NSInvocation, but fast enough to be irrelevant given that this is gonna cause drawing -- and a heck of a lot more flexible. I could easily see refactoring the above into driving, say, CoreAnimation.

Copied from an answer by Matt Ball:
- (void)startMyTimer {
/* ... Some stuff ... */
NSDictionary *userDict;
userDict = [NSDictionary dictionaryWithObjectsAndKeys:someValue,
#"value1",
someOtherValue,
#"value2", nil];
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(callMyMethod:)
userInfo:userDict
repeats:YES];
}
- (void)callMyMethod:(NSTimer *)theTimer {
NSString *value1 = [[theTimer userInfo] objectForKey:#"value1"];
NSString *value2 = [[theTimer userInfo] objectForKey:#"value2"];
[self myMethod:value1 setValue2:value2];
}

If you use a target-action timer, you can't have the timer directly call an arbitrary method. A timer's action must have a very specific signature. You can pass additional data in the userinfo dictionary and have the timer's action call the method you ultimately want, or you can use the invocation form as Dave said. Personally, I usually do the former, because I find NSInvocations to be annoying and setting one up can actually take more code than just writing an intermediary method.

You can pass an NSDictionary*, or some other object, as the userInfo and put the arguments in that.

As an alternative to NSTimer, on iOS 4.0+ and 10.6+, you could use Grand Central Dispatch and dispatch sources to do this using a block. Apple has the following code for this in their Concurrency Programming Guide:
dispatch_source_t CreateDispatchTimer(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block)
{
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer)
{
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer;
}
You could then set up a one-second timer event using code like the following:
dispatch_source_t newTimer = CreateDispatchTimer(1ull * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10, dispatch_get_main_queue(), ^{
[self setX:someValue andY:otherValue andObject:obj];
});
as long as you store and release your timer when done. This can even let you trigger a timer to execute items on a background thread by using a concurrent queue instead of the main queue used above.
This can avoid the need for boxing and unboxing arguments.

Create dictionary with those arguments and pass that dictionary with timer userinfo. That will solve your problem

Related

accessing instance variable in cocos2d scheduled method crashes

fresh to objC and cocos2d :)
i'm following "learn cocos2d game development with iOS5", in chapter4, there is a "DoodleDrop" game.
define some variable in GameScene.h like this
#interface GameScene : CCLayer
{
CCSprite *player;
CGPoint playerVelocity;
CCArray *spiders;
CGSize screenSize;
int dropedSpidersCount;
float duration;
}
+ (CCScene *)scene;
#end
in GameScene.m the init method looks like this
- (id)init
{
if (self = [super init]) {
duration = 4.0;
[self createPlayer];
[self createSpiders]; // spiders were inited here.
[self resetSpiders];
[self schedule:#selector(chooseSpider:) interval:0.7];
}
return self;
}
while in chooseSpider, i cannot access spiders, xcode broke
in other methods, spiders or duration just behave normally, why does this happens?
gist code added
https://gist.github.com/2940466
After inspecting your code, I suggest you to try this fix:
- (void)createSpiders
{
CCSprite *tempSpider = [CCSprite spriteWithFile:#"spider.png"];
CGSize spiderSize = [tempSpider texture].contentSize;
int spiderCount = screenSize.width / spiderSize.width;
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
for (int i = 0; i < spiderCount; i++) {
CCSprite *spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider];
[spiders addObject:spider];
}
}
where the only difference is in the line:
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
Indeed, if you do not retain you spiders object, it will be autoreleased at the next run loop iteration.
OLD ANSWER:
Without seeing more code it is not possible to say exactly what is happening, but it seems that in the interval between creating the spiders and the actual execution of chooseSpiders, your spiders array gets deallocated.
As a quick try, I would suggest adding:
[spiders retain];
before calling
[self schedule:#selector(chooseSpider:) interval:0.7];
and see wether the crash keeps happening.
if you provide more code, it could be possible to help you further.

Stop performing the animation in background thread and run loop

I run my animations in a UITAbleViewCell.
Each cell has its own animation and the cells are reusable.
I use [mView performSelectorInBackground:#selector(layoutSubview) withObject:nil];
There in the background thread I initiate the runLoop to perform tasks like this:
- (void)startAnimation
{
NSRunLoop *mLoop = [NSRunLoop currentRunLoop];
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:#selector(setNeedsDisplay) userInfo:nil repeats:YES];
mRunLoop = YES;
while (mRunLoop == YES && [mLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]);
}
and stop it:
- (void)stopAnimation
{
if (![NSThread isMainThread]) {
[[NSThread currentThread] cancel];
}
mRunLoop = NO;
self.animationTimer = nil;
CFRunLoopStop(CFRunLoopGetCurrent());
}
I run into problems when I fast scroll through table, because on the first cell initiation I begin the animation, so the first runLoop call occures which performs a setNeedDisplay and all the methods from it. But before finishing the first runLoop cycle the cell disappears from the view and is already available for reuse. So I begin clearing it, while the cycle is still performing operations and here I meet situations like
message sent to deallocated instance
So could you please give me some hints of how should I correctly stop performing the operations in that thread? I mean if I want to realese for example an object, which is performing some actions how to immediately stop'em?
Hope I gave enough info.
Thanks
UPDATE: No ideas at all?
I'll take a completely different stab on it:
Get rid of the cell's timers and background threads altogether!
Animation is not something where NSTimer is a good fit in the first place and having multiple timers won't help much, either.
UITableView has a method visibleCells and a method indexPathsForVisibleRows. I'd suggest to use a single CADisplayLink — which is suited for animation, as it calls you back with the actual refresh rate of the display or a fraction thereof — in your tableview-controller and in the callback of that display-link iterate over the visible cells.
If you want to schedule the display-link on the run-loop of a secondary thread, feel free to do so, but I'd check if you can get away without extra threading first.
Some code:
#interface AnimatedTableViewController ()
#property (strong, nonatomic) CADisplayLink *cellAnimator;
- (void)__cellAnimatorFired:(CADisplayLink *)animator;
#end
#implementation AnimatedTableViewController
#synthesize cellAnimator = cellAnimator_;
- (void)setCellAnimator:(CADisplayLink *)animator
{
if (animator == cellAnimator_)
return;
[cellAnimator_ invalidate];
cellAnimator_ = animator;
[cellAnimator_ addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSCommonRunLoopModes];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.cellAnimator = [CADisplayLink displayLinkWithTarget:self selector:#selector(__cellAnimatorFired:)];
...
}
- (void)viewWillDisappear:(BOOL)animated
{
self.cellAnimator = nil;
...
[super viewWillDisappear:animated];
}
- (void)__cellAnimatorFired:(CADisplayLink *)animator
{
NSArray *visibleCells = [self.tableView visibleCells];
[visibleCells enumerateObjectsUsingBlock:^(UITableViewCell *cell, NSUInteger unused, BOOL *stop){
[cell setNeedsDisplay];
}];
}
...
#end
NSTimer has a -cancel method that stops the timer from firing. Calling it in -prepareForReuse (and, for that matter, in -stopAnimation) may help.
However, this code looks rather dangerous. Nesting run loops like this is almost never a good idea—and moreover, as far as I can tell it's totally unnecessary. If you let -startAnimation return, your animation timer will still get run on the main run loop. And if you're doing it this way because there's some code after -startAnimation that you want to delay, you should restructure your code so this isn't needed.
(If you drop the runloop stuff in -startAnimation, don't stop the runloop in -stopAnimation either.)
Something like the approach danyowdee recommends would be even better, but at least get rid of this runloop stuff. It's just asking for trouble.
I think you can use this method for your problem
[NSObject cancelPreviousPerformRequestsWithTarget:yourTarget selector:aSelector object: anArgument];
I think that the best way to avoid that behavior is assigning the delegate that receives the cancel method in other class that won't be reused. For example, you can have a private array of instances that process all the cancel methods, each row mapped into an array element.
I recommend you the lazy tables example provided by Apple in Xcode documentation. It's a great example of how to load images asynchroniously in background with a table. I think that also it would be useful for you for the scrolling subjects (decelerating and paging).
Only one more consideration, i don't recommend messing up with several cfrunloopstop, test it hard!

NSTimer invalidate not working

I am trying to create an explosion on the iphone screen which gets bigger fast, then goes away. Why is this timer not stopping?
NSTimer *explosion = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(explosion) userInfo:nil repeats:YES];
-(void)explosion {
image.image = [UIImage imageNamed:#"explosion.png"];
expsize = expsize + 2.5;
image.frame = CGRectMake(image.frame.origin.x, image.frame.origin.y, expsize, expsize);
if (expsize > 60) {
NSLog(#"%f",expsize);
[explosion invalidate];
explosion = nil;
}
}
You are most likely invalidating the wrong timer.
You create a local variable named explosion that has the same name as the instance variable.
Avoid declaring instance variables and local variables with the same name!
I'd suggest that you use the form of selector that the NSTimer doc calls for: - (void)timerFireMethod:(NSTimer*)theTimer. The you can invalidate "theTimer" and be sure you're invalidating the right one.
Also, of course, if "explosion" is declared as a property, then there will be two methods in the class named "explosion", and no real clue as to which one is getting called.
It's hard to be certain, because it's not clear whether this is exactly your code, but you have two variables named explosion, and one of them has an NSTimer assigned to it; the other (which seems to be an ivar) is nil.
// Variable local to whatever method this is in
NSTimer *explosion = [NSTimer scheduledTimerWithTimeInterval:0.1...
if (expsize > 60) {
NSLog(#"%f",expsize);
// Other variable named "explosion" does not exist.
// This is an ivar? Has not been set.
[explosion invalidate];
Assuming you've got explosion declared as a property (and there is no reason not to), you should fix this by using the setter when you create the timer:
[self setExplosion:[NSTimer scheduledTimerWithTimeInterval:...]];
Now the ivar has the timer instance and you can use it to invalidate the timer.
Also, note that your timer's method is incorrect; it must take one parameter which is a pointer to the timer. You can also use this pointer to invalidate the timer when it fires.
- (void) fireExplosion: (NSTimer *)tim {
//...
if( expsize > 60 ){
[tim invalidate];
//...
}
}
Finally, you have one last naming problem; if your property is called explosion, the convention in Cocoa is that the accessor should have the same name, but you have used explosion for the method that your timer calls. This could cause hard-to-track problems later. You should rename the timer method as I have here, using a verb indicating that something is happening.
If you are declaring explosion how you posted in your example then you are shadowing your instance variable explosion. As a word of advice you should use a naming convention for instance variables such as an underscore prefix. Now keeping track of the timer is not required if you only invalidate it after it fires. You could just take an extra parameter on the explosion method that would be the timer explosion:(id)timer. Otherwise you can do the following.
#interface X : NSObject
{
NSTimer *_explosion;
}
#end
And when you go to declare it in your code do the following
...
[_explosion invalidate];
[_explosion release];
//There is a whole 'nother debate on whether or not to retain a scheduled timer
//but I am a stickler for ownership so remember to release this in dealloc
_explosion = [[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(explosion)
userInfo:nil
repeats:YES] retain];
...
-(void)explosion {
image.image = [UIImage imageNamed:#"explosion.png"];
expsize = expsize + 2.5;
image.frame = CGRectMake(image.frame.origin.x, image.frame.origin.y, expsize, expsize);
if (expsize > 60) {
NSLog(#"%f",expsize);
[_explosion invalidate];
[_explosion release];
_explosion = nil;
}
}

Repeating NSTimer, weak reference, owning reference or iVar?

I thought I would put this out here as a separate question from my previous
retaining-repeating-nstimer-for-later-access as the discussion has moved forward making a new question clearer than yet another EDIT:
The scenario is an object creates a repeating NSTimer, lets say in viewDidLoad, once created the NSTimer needs to stay around so it can be accessed by other methods.
NSTimer *ti = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateDisplay:)
userInfo:nil
repeats:YES];
I understand that when created the runloop takes ownership of the NSTimer and ultimately stops, removes and releases the NSTimer when [ti invalidate]; is called.
By virtue of the fact that we need to access the NSTimer in more than one method we need some way to hold a reference for future use, the revised question is:
// (1) Should the NSTimer be held using an owning reference (i.e.)
#property(nonatomic, retain) NSTimer *walkTimer;
[self setWalkTimer: ti];
...
...
// Cancel method
[[self walkTimer] invalidate;
[self setWalkTimer:nil];
...
...
// dealloc method
[walkTimer release];
[super dealloc];
.
// (2) Should the NSTimer be held using a weak reference (i.e.)
#property(nonatomic, assign) NSTimer *walkTimer;
[self setWalkTimer: ti];
...
...
// Cancel method
[[self walkTimer] invalidate];
[self setWalkTimer:nil];
...
...
// dealloc method
[super dealloc];
.
// (3) Use an iVar and rely on the runLoop holding (i.e. retaining) the timer
NSTimer *walkTimer;
NSTimer *walkTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(updateDisplay:)
userInfo:nil
repeats:YES];
...
...
// Cancel method
[walkTimer invalidate];
walkTimer = nil;
.
// (4) Something not listed above ...
I am happy for just (1) (2) (3) or (4) as a lot of discussion regarding which is best has already been written on the Other thread. There does seem to be a lot of conflicting answers so I hope this more specific question will help focus on what might be best practice in this situation.
EDIT:
As a side note in the Apple NSTimer Class Reference 4 out of 5 of the sample code projects use NSTimers that are assigned** to a retained property. Here is an example of what the class reference examples show:
#property (nonatomic, retain) NSTimer *updateTimer;
updateTimer = [NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:#selector(updateCurrentTime) userInfo:p repeats:YES];
...
...
// Cancel
[updateTimer invalidate];
updateTimer = nil;
...
...
// Dealloc method
[super dealloc];
[updateTimer release];
** It should be noted that in the examples Apple are assigning the iVar directly and not using the property setter.
After giving it all some more thought and finding an important flaw in my reasoning, I've come to a different conclusion:
It doesn't matter much, whether you hold an owning or a non-owning reference to a timer that you need to invalidate. It is completely a matter of taste.
The deal breaker is, what the target of the timer is:
If the object that creates a timer is its target, managing that object's lifetime becomes more fragile: it cannot simply be retain/release managed, instead you need to ensure that the client that holds the last reference to this object makes it invalidate the timer before it disposes of it.
Let me illustrate the situation with a couple of sort-of-object-graphs:
You start in a state from which you setup the timer and set yourself as the target. Setup of the Timer: yourObject is owned by someClientObject. In parallel exists the current run-loop with an array of scheduledTimers. the setupTimer method is called upon yourObject:
The result is the following initial state. In addition to the former state yourObject now has a reference (owned or not) to the workTimer, which in turn owns yourObject. Furthermore, workTimer is owned by the run-loops scheduledTimers array:
So now you'll use the object, but when you're done with it and simply release it, you'll end up with simple release leak: after someClientObject disposes of yourObject through a simple release, yourObject is disassociated from the object-graph but kept alive by workTimer. workTimer and yourObject are leaked!
Where you leak the object (and the timer) because the runloop keeps the timer alive, which — in turn — keeps an owning reference to your object.
This can be avoided if yourObject is only ever owned by one single instance at a time, when it is properly disposed of proper disposal through cancellation: before disposing of yourObject through release, someClientObject calls the cancelTimer method on yourObject. Within that method, yourObject invalidates workTimer and (if it owned workTimer) disposes of workTimer through release:
But now, how do you resolve the following situation?
Multiple Owners: Setup like in the initial state, but now with multiple independent clientObjects that hold references to yourObject
There is no easy answer, I am aware of! (Not that the latter has to say much, but...)
So my advice is...
Don't make your timer a property/don't provide accessors for it! Instead, keep it private (with the modern runtime I think you could go so far as to define the ivar in a class extension) and only deal with it from one single object. (You may retain it, if you feel more comfortable doing so, but there is absolutely no need for it.)
Caveat: If you absolutely need to access the timer from another object, make the property retain the timer (as that is the only way to avoid crashes caused by clients that directly invalidated the timer they accessed) and provide your own setter. Rescheduling a timer is — in my opinion — not a good reason to break encapsulation here: provide a mutator if you need to do that.
Set the timer up with a target other than self. (There are plenty of ways doing so. Maybe through writing a generic TimerTarget class or — if you can use it — through a MAZeroingWeakReference?)
I apologize for being a moron in the first discussion and want to thank Daniel Dickison and Rob Napier for their patience.
So here is the way I am going to handle timers from now on:
// NSTimer+D12WeakTimerTarget.h:
#import <Foundation/NSTimer.h>
#interface NSTimer (D12WeakTimerTarget)
+(NSTimer *)D12scheduledTimerWithTimeInterval:(NSTimeInterval)ti weakTarget:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat logsDeallocation:(BOOL)shouldLogDealloc;
#end
// NSTimer+D12WeakTimerTarget.m:
#import "NSTimer+D12WeakTimerTarget.h"
#interface D12WeakTimerTarget : NSObject {
__weak id weakTarget;
SEL selector;
// for logging purposes:
BOOL logging;
NSString *targetDescription;
}
-(id)initWithTarget:(id)target selector:(SEL)aSelector shouldLog:(BOOL)shouldLogDealloc;
-(void)passthroughFiredTimer:(NSTimer *)aTimer;
-(void)dumbCallbackTimer:(NSTimer *)aTimer;
#end
#implementation D12WeakTimerTarget
-(id)initWithTarget:(id)target selector:(SEL)aSelector shouldLog:(BOOL)shouldLogDealloc
{
self = [super init];
if ( !self )
return nil;
logging = shouldLogDealloc;
if (logging)
targetDescription = [[target description] copy];
weakTarget = target;
selector = aSelector;
return self;
}
-(void)dealloc
{
if (logging)
NSLog(#"-[%# dealloc]! (Target was %#)", self, targetDescription);
[targetDescription release];
[super dealloc];
}
-(void)passthroughFiredTimer:(NSTimer *)aTimer;
{
[weakTarget performSelector:selector withObject:aTimer];
}
-(void)dumbCallbackTimer:(NSTimer *)aTimer;
{
[weakTarget performSelector:selector];
}
#end
#implementation NSTimer (D12WeakTimerTarget)
+(NSTimer *)D12scheduledTimerWithTimeInterval:(NSTimeInterval)ti weakTarget:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat logsDeallocation:(BOOL)shouldLogDealloc
{
SEL actualSelector = #selector(dumbCallbackTimer:);
if ( 2 != [[target methodSignatureForSelector:aSelector] numberOfArguments] )
actualSelector = #selector(passthroughFiredTimer:);
D12WeakTimerTarget *indirector = [[D12WeakTimerTarget alloc] initWithTarget:target selector:selector shouldLog:shouldLogDealloc];
NSTimer *theTimer = [NSTimer scheduledTimerWithTimeInterval:ti target:indirector selector:actualSelector userInfo:userInfo repeats:shouldRepeat];
[indirector release];
return theTimer;
}
#end
Original (for full disclosure):
You know my opinion from your other post:
There is little reason for an owning reference of a scheduled timer (and bbum seems to agree).
That said, your options 2, and 3 are essentially the same. (There is additional messaging involved in [self setWalkTimer:nil] over walkTimer = nil but I'm not sure if the compiler won't optimize that away and access the ivar directly, but well...)
I generally manage the invalidate inside of the accessor so that you never get surprised by a timer accessing you after you think you got rid of it:
#property(nonatomic, retain) NSTimer *walkTimer;
[self setWalkTimer: ti];
- (void)setWalkTimer:(NSTimer *)aTimer
{
if (aTimer != walkTimer_)
{
[aTimer retain];
[walkTimer invalidate];
[walkTimer release];
walkTimer = aTimer;
}
}
...
...
// Cancel method
[self setWalkTimer:nil];
...
...
// Make a new timer, automatically invalidating the old one
[self setWalkTimer:[... a new timer ...]]
...
...
// dealloc method
[walkTimer_ invalidate];
[walkTimer_ release];
[super dealloc];

Is this a good way to do a game loop for an iPhone game?

I'm new to iPhone dev, but trying to build a 2D game. I was following a book, but the game loop it created basically said:
function gameLoop
update()
render()
sleep(1/30th second)
gameLoop
The reasoning was that this would run at 30fps. However, this seemed a little mental, because if my frame took 1/30th second, then it would run at 15fps (since it'll spend as much time sleeping as updating).
So, I did some digging and found the CADisplayLink class which would sync calls to my gameLoop function to the refresh rate (or a fraction of it). I can't find many samples of it, so I'm posting here for a code review :-) It seems to work as expected, and it includes passing the elapsed (frame) time into the Update method so my logic can be framerate-independant (however I can't actually find in the docs what CADisplayLink would do if my frame took more than its allowed time to run - I'm hoping it just does its best to catch up, and doesn't crash!).
//
// GameAppDelegate.m
//
// Created by Danny Tuppeny on 10/03/2010.
// Copyright Danny Tuppeny 2010. All rights reserved.
//
#import "GameAppDelegate.h"
#import "GameViewController.h"
#import "GameStates/gsSplash.h"
#implementation GameAppDelegate
#synthesize window;
#synthesize viewController;
- (void) applicationDidFinishLaunching:(UIApplication *)application
{
// Create an instance of the first GameState (Splash Screen)
[self doStateChange:[gsSplash class]];
// Set up the game loop
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(gameLoop)];
[displayLink setFrameInterval:2];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void) gameLoop
{
// Calculate how long has passed since the previous frame
CFTimeInterval currentFrameTime = [displayLink timestamp];
CFTimeInterval elapsed = 0;
// For the first frame, we want to pass 0 (since we haven't elapsed any time), so only
// calculate this in the case where we're not the first frame
if (lastFrameTime != 0)
{
elapsed = currentFrameTime - lastFrameTime;
}
// Keep track of this frames time (so we can calculate this next time)
lastFrameTime = currentFrameTime;
NSLog([NSString stringWithFormat:#"%f", elapsed]);
// Call update, passing the elapsed time in
[((GameState*)viewController.view) Update:elapsed];
}
- (void) doStateChange:(Class)state
{
// Remove the previous GameState
if (viewController.view != nil)
{
[viewController.view removeFromSuperview];
[viewController.view release];
}
// Create the new GameState
viewController.view = [[state alloc] initWithFrame:CGRectMake(0, 0, IPHONE_WIDTH, IPHONE_HEIGHT) andManager:self];
// Now set as visible
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void) dealloc
{
[viewController release];
[window release];
[super dealloc];
}
#end
Any feedback would be appreciated :-)
PS. Bonus points if you can tell me why all the books use "viewController.view" but for everything else seem to use "[object name]" format. Why not [viewController view]?
You have Cocos2D listed as a tag in your question but you're not actually using any Cocos2D code. Have you considered doing a Cocos2D implementation for your games? It will save you some unneeded hassle.
As for your syntax question [myObject view] is used for calling methods on myObject while myObject.view is used for setting/getting instance variables exposed as properties. I don't recall if you can retrieve instance variables using [myObject view] as well but if that works then I guess the only difference between the two is the syntax and you could use both methods to retrieve instance variables.
Hope some of that rambling is useful to you.
From many GL example by Apple, I think you should use a timer.
animationTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0/60.0)
target:self
selector:#selector(updateAndRender)
userInfo:nil
repeats:TRUE];