Why memory consumption grows? - objective-c

Here is a very simple piece of code:
- (void)functionOne
{
[self performSelector:#selector(functionTwo) withObject:nil afterDelay:1.0];
}
- (void)functionTwo
{
[self performSelector:#selector(functionOne) withObject:nil afterDelay:1.0];
}
As you can see there's nothing in these two methods what could cause growth of memory consumption. But it grows. Very slowly, but it does. About 0.01 MB every three seconds. Why? How can i avoid it?

You are effectively creating an infinite loop. If you want to switch the state of an object every one second (as you said in the comments), do it this way:
Create a method like so:
- (void)functionOne
{
if( [obj isEqual:stateA] ) {
obj = stateB;
} else {
obj = stateA;
}
}
and call it with a timer:
NSTimer* myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self
selector:#selector(functionOne) userInfo:nil repeats:YES];

to avoid infinite loops you should use a NSTimer and one function, in which you toggle the state of your object.
in your init method, or something like viewDidLoad you should start a timer like
- (void)viewDidLoad {
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(switchState:)
userInfo:nil
repeats:YES];
}
then you use one method like
- (void) switchState:(NSTimer *)timer {
if ([[self yourState] isEqual:stateOne]) {
[self setYourState:stateTwo];
} else {
[self setYourState:stateOne];
}
}
for more information see http://developer.apple.com/library/ios/#documentation/cocoa/reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html

The memory grows simply because the Application needs to hold onto some state. Each time you call perform selector that selector is pushed onto the stack. This stack is saved in memory. Hence the growth.

Related

How do I set a NSTimer to stop if a certain action is running?

I have a popup in a custom view controller that is presented after 1 minute thanks to my NSTimer.
My NSTimer code:
[NSTimer scheduledTimerWithTimeInterval:60.0f
target:self selector:#selector(methodB:) userInfo:nil repeats:YES];`
The method that reveals the popup
- (void) methodB:(NSTimer *)timer
{
//Do calculations.
[self showPopupWithStyle:CNPPopupStyleFullscreen];
}
I'm trying to set the NSTimer to stop running if the [self showPopupWithStyle:CNPPopupStyleFullscreen]; is currently open, running or active.
Then I would like to start the NSTimer back up again if the popup view controller is NOT open, running or active.
Any help, samples or examples would be greatly appreciated!
My project is written in Objective-C.
EDIT:
I tried the answers in the suggested "possibly similar" answer and it doesn't seem to work for what I am doing. How do I stop NSTimer?
This seems to do the trick.
#property (nonatomic, strong) CNPPopupController *popupController;
- (void)_timerFired:(NSTimer *)timer;
#end
NSTimer *_timer;
- (void)viewDidLoad {
[super viewDidLoad];
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:10.0f
target:self
selector:#selector(_timerFired:)
userInfo:nil
repeats:YES];
}
}
- (void)_timerFired:(NSTimer *)timer {
if ([_timer isValid]) {
[self showPopupWithStyle:CNPPopupStyleFullscreen];
[_timer invalidate];
}
_timer = nil;
NSLog(#"ping");
}
If you want to restart the timer just add this code where you'd like the action to start back up.
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:10.0f
target:self
selector:#selector(_timerFired:)
userInfo:nil
repeats:YES];
}
Hope this helps! :)

Fire IBAction automatically

I have a button and want to fire it automatically by itself without touch.
Is it possible?
-(IBAction)xxx:(id)sender
My answer assumes you have the method:
- (IBAction)someAction:(UIButton *)sender {
}
and that you have a reference to the button in an instance variable named someButton.
If you just need to "fire it" now, simply call it:
[self someAction:someButton];
If you need to "fire it" once, but later, you can do:
// call it 5 seconds from now
[self performSelector:#selector(someAction:) withObject:someButton afterDelay:5.0];
If you want to fire it repeatedly, use a timer:
myTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(buttonTimerFired) userInfo:nil repeats:YES];
- (void)buttonTimerFired {
[self someAction:someButton];
}
Action can be called like every regular function - you can do it by running a timer on something else.
You should use NSTimer for doing your work.
[NSTimer scheduledTimerWithTimeInterval: 0.01f target: self selector: #selector(BtoonMethod) userInfo: nil repeats: NO];
-(void)BtoonMethod
{
// write code for call yor button method
}

How to stop NSTimer started on button click in this timer callback method

I am having problems with stoping NSTimer started on button click. [timer invalidate] and timer = nil; just do nothing neither when I am trying to stop it viewWillDisappear nor in method invoked by method which is being invoked by this timer. However when I start my timer in viewWillAppear and invalidate it in viewWillDisappear everything is fine.
I suppose there might be an issue in thread I am starting the timer from. Can you help with that?
I looked through all answers here regarding NSTimer not stopping, but they didn't help to solve the problem.
The way I initialize my timer:
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(oneSecondPassedSinceRoundStarted) userInfo:nil repeats:YES];
The ways I tried to stop it:
[self.timer invalidate];
[timer invalidate];
It's funny how quick you can answer your own question after you asked for a help. No matter how long you have been struggling to find the answer before by yourself:
[self performSelectorOnMainThread:#selector(stopTimer) withObject:nil waitUntilDone:YES];
And the selector:
- (void) stopTimer
{
[timer invalidate];
timer = nil;
}
I have created and tested this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(oneSecondPassedSinceRoundStarted:) userInfo:nil repeats:YES];
}
-(void)oneSecondPassedSinceRoundStarted:(NSTimer *)time {
// Do what You want
NSLog(#"CALLING!");
}
-(IBAction)buttonAction:(id)sender {
NSLog(#"Stop timer!");
[myTimer invalidate];
myTimer = nil;
}
And it is working nice.
Note: You forget to add colon after oneSecondPassedSinceRoundStarted.

NSTimer causes "unrecognized selector" crash when it fires

I'm using a NSTimer to run an animation (for now just call it myMethod). However, its causing a crash.
Here's the code:
#implementation SecondViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void) myMethod
{
NSLog(#"Mark Timer Fire");
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"We've loaded scan");
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(myMethod:)
userInfo:nil
repeats:YES];
animationTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(myMethod:) userInfo:nil repeats: YES];
}
And here's the output during the crash
-[SecondViewController myMethod:]: unrecognized selector sent to instance 0x4b2ca40
2012-06-21 12:19:53.297 Lie Detector[38912:207] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SecondViewController myMethod:]: unrecognized selector sent to instance 0x4b2ca40'
So what am I doing wrong here?
I ran into this problem while using Swift. It may not be evident that in Swift I discovered that the target object of NSTimer must be an NSObject.
class Timer : NSObject {
init () { super.init() }
func schedule() {
NSTimer.scheduledTimerWithTimeInterval(2.0,
target: self,
selector: "myMethod",
userInfo: nil,
repeats: true)
}
func myMethod() {
...
}
}
Hope this helps somebody.
either you can use only
- (void)myMethod: (id)sender
{
// Do things
}
or you can do (remove : from both the method name)..
animationTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target:self selector:#selector(myMethod) userInfo:nil repeats: YES];
hope this will help you
replace this
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(myMethod:)
userInfo:nil
repeats:YES];
by this
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(myMethod)
userInfo:nil
repeats:YES];
The timer's action method should take one argument:
- (void)myMethod: (NSTimer *)tim
{
// Do things
}
The name of this method is myMethod:, including the colon. Your current method's name is myMethod, without the colon, but you create your timer by passing a method name that has it: selector:#selector(myMethod:).
Currently, then, the timer sends the message myMethod: to your object; your object doesn't respond to that (but would respond to myMethod) and raises an exception.

NSTimer's action method is not being called

I have the following code to create an NSTimer which should update a label each time it fires:
.h file
#interface Game : UIViewController
{
NSTimer *updateTimer;
UILabel *testLabel;
int i;
}
-(void)GameUpdate;
#end
.m file
#implementation Game
-(void)GameUpdate
{
i++;
NSString *textToDisplay = [[NSString alloc] initWithFormat:#"Frame: %d", i];
[testLabel setText:textToDisplay];
NSLog(#"Game Updated");
}
- (void)viewDidLoad
{
[super viewDidLoad];
[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
}
//other methods (viewDidUnload, init method, etc.)
#end
When I run it, a label appears in the top that says "0" but does not change. It makes me believe I missed something in how the NSTimer is to be setup. What did I miss?
I used breakpoints and (as you can see) logging to see if the method is actually running, rather than some other error.
I was having a similar problem, and it had a different root cause, related to the run loop. It's worth noting that when you schedule the Timer with the code:
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
The timer will get scheduled with the current thread's runLoop. In your case, because you make this call within the viewDidLoad, it is the main thread, so you are are good to go.
However, if you schedule your timer with a thread other than the main thread, it will get scheduled on the runLoop for that thread, and not main. Which is fine, but on auxiliary threads, you are responsible for creating and starting the initial run loop, so if you haven't done that - your callback will never get called.
The solution is to either start the runLoop for your auxiliary thread, or to dispatch your timer start onto the main thread.
to dispatch:
dispatch_async(dispatch_get_main_queue(), ^{
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
});
To start a runloop:
After creating a thread using your API of choice, call CFRunLoopGetCurrent() to allocate an initial run loop for that thread. Any future calls to CFRunLoopGetCurrent will return the same run loop.
CFRunLoopGetCurrent();
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:#selector(GameUpdate) userInfo:nil repeats:YES];
Your callback must have this signature:
-(void)GameUpdate:(NSTimer *)timer
This is explicitly in the docs. And the #selector() reference when you setup the timer should be #selector(GameUpdate:) (notice the trailing :).
Try that.
Just in case anyone stumbles across this, I want to point out that this:
[[NSString alloc] initWithFormat:#"Frame: %d", i];
Needs memory management.
Safely replace with:
[NSString stringWithFormat:#"Frame: %d", i];
for the same effect but no need for memory management.
P.S. At time of writing I cannot comment on the original post, so I've added this as an answer.
EDIT: As adam waite pointed out below, this isn't really relevant anymore with the widespread usage of ARC.
I have had a little bit different issue with NSTimer - scheduled method call was ignored during UITableView scrolling.
Timer had been started from main thread. Adding timer explicitly to main run loop resolved the problem.
[[NSRunLoop mainRunLoop] addTimer:playbackTimer forMode:NSRunLoopCommonModes];
Solution found here https://stackoverflow.com/a/2716605/1994889
UPD: CADisplayLink fits much better for updating UI.
According official documentation, CADisplayLink is a:
Class representing a timer bound to the display vsync.
And can be easily implemented like:
playbackTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(updateUI)];
[playbackTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
and removed like
if (playbackTimer) {
[playbackTimer removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
playbackTimer = nil;
}