there is a nib file and i am creating different window instances with different context, all controls works normally except timer and variables triggered by timers looks shared with all windows. this is how i am create window instances.
#import <Cocoa/Cocoa.h>
#import "MYWindow.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
}
#property (strong) MYWindow *pickerWindow;
--
#import "AppDelegate.h"
#implementation AppDelegate
-(IBAction)newWindow:(id)sender
{
myWindow = [[MYWindow alloc] initWithWindowNibName:#"MYWindowNIB"];
[myWindow showWindow:self];
}
Also i am having problem with ARC, when i open new instance of a window previous one releases immediately even i declare it is strong in property that is why compiling AppDelegate with a flag -fno-objc-arc. otherwise as i said what ever i did windows releases immediately.
XCode 4.6
edit
int i = 0;
-(void)windowDidLoad
{
timerMoveOutNavBar = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countUP) userInfo:nil repeats:YES];
}
-(void)countUP
{
[text setStringValue:[NSString stringWithFormat:#"%d", i]];
i++;
}
i found the solution, you have to declare variables and all other objects as private.
#private
int i;
What do you mean by "share same variables"? You can have a superclass that all the window controllers inherit from, and they will all have the properties and methods you create in the superclass, if that's what you're talking about.
As far as the windows being released, do you have the "Release When Closed" box checked in IB? If so, uncheck that box.
After Edit
The problem has to do with the way you initialize the int variable "i". By putting it outside a method, you're declaring it as a global variable that all instance will see. You should create an ivar and set its value to zero where you create the timer:
#implementation MyWindowController {
IBOutlet NSTextField *text;
int i;
}
- (void)windowDidLoad {
[super windowDidLoad];
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(countUP:) userInfo:nil repeats:YES];
i = 0;
}
-(void)countUP:(NSTimer *) timer {
[text setStringValue:[NSString stringWithFormat:#"%d", i]];
i++;
if (i== 50) [timer invalidate];
}
Notice that I added a colon to the selector name -- with a timer, the timer passes itself as the argument to its selector, so you can reference it like I do to invalidate it. But it's ok to do it the way you did by assigning the timer to an ivar or property (timerMoveOutNavBar in your case).
Related
I have a part in my project, where I have to fill many arrays and dictionaries with custom classes. During the process the memory usage starts to increase of course. But at the end when I remove items from everywhere using removeAllObjects, the memory usage remains on the same level instead of decreasing to the starting value.
I made a simplified code which does not make sense of course but reproduces the original issue I am getting:
Header:
#interface ViewController : UIViewController
#property (strong, nonatomic) NSMutableArray *imageArray;
#property (strong, nonatomic) NSTimer *timer;
- (IBAction)start:(id)sender;
- (IBAction)stop:(id)sender;
#end
Implementation:
#implementation ViewController
....
- (IBAction)start:(id)sender
{
[self.loadingIndicator startAnimating];
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(addImages) userInfo:nil repeats:YES];
}
- (IBAction)stop:(id)sender
{
[self.loadingIndicator stopAnimating];
[self.timer invalidate];
[self.imageArray removeAllObjects];
}
- (void)addImages
{
int i = 0;
while (i < 500)
{
UIImage *image = [UIImage imageNamed:#"test.png"];
[self.imageArray addObject:image];
i++;
}
}
...
#end
Issue:
When I call start, after 1 min the memory usage increases from 3.9 MB to 58 MB. It is fine, but when I call stop, where removelAllObjects is called, it is still remains the same value, 58 MB.
What am I doing wrong? I am using ARC!
Images created with "imageNamed:" are cached, and if nobody else has a strong reference, then the operating system will release them when it feels it is a good idea to do it.
That avoids situations where you call imageNamed: repeatedly and each time a new image is created, which is rather expensive.
I have problem with NSTimer in Objective-C. This is my source code:
Main.m
#import <Foundation/Foundation.h>
#import "TimerTest.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
TimerTest *timerTest = [[[TimerTest alloc] init] autorelease];
}
return 0;
}
TimerTest.h
#import <Foundation/Foundation.h>
#interface TimerTest : NSObject {
NSTimer *_timer;
}
#property (nonatomic, retain) NSTimer *timer;
- (id) init;
#end
TimerTest.m
#import "TimerTest.h"
#implementation TimerTest
#synthesize timer = _timer;
- (id) init {
if (self = [super init]) {
[NSTimer timerWithTimeInterval:0.5f
target:self
selector:#selector(tick:)
userInfo:nil
repeats:YES];
}
return self;
}
- (void) tick: (NSDate *) dt {
NSLog(#"Tick! \n");
}
- (void) dealloc {
self.timer = nil;
[super dealloc];
}
#end
My program should log "Tick! \n" every 0.5 second. But then my program is finished, xcode console is clear, that's meaning that NSLog in
-(void)tick:(NSDate *)dt method didn't work . Where is my mistake?
My program should log "Tick! \n" every 0.5 second.
No it shouldn't (at least not according to the code you posted). You need a run loop. Timers only fire as events on run loops. So, in your main, you need to set one up and run it.
Not only do you need an event loop, but you've created a timer, and you haven't scheduled it in said run loop. Instead of:
[NSTimer timerWithTimeInterval:0.5f
target:self
selector:#selector(tick:)
userInfo:nil
repeats:YES];
do this:
[NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:#selector(tick:)
userInfo:nil
repeats:YES];
I set your code up in the context of a Cocoa application (because it comes with a run loop), executing the TimerTest allocation in the applicationDidFinishLaunching of the delegate, and your code otherwise works.
A couple of other things: The method whose selector is passed to scheduledTimerWithTimerInterval:... should be of the form
- (void)timerMethod:(NSTimer *)aTimer
and when you're done with your timer, just invalidate it:
[timer invalidate];
though you have to keep a reference to the timer to do this, which it appears you hadn't.
The following is my Objective-C category on NSTimer to do block-based firing of NSTimers. I can't see anything wrong with it, but what I am getting is that the block I pass into the schedule... method is being deallocated despite me calling copy on it.
What am I missing?
typedef void(^NSTimerFiredBlock)(NSTimer *timer);
#implementation NSTimer (MyExtension)
+ (void)timerFired:(NSTimer *)timer
{
NSTimerFiredBlock blk = timer.userInfo;
if (blk != nil) {
blk(timer);
}
}
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
repeats:(BOOL)repeats
callback:(NSTimerFiredBlock)blk
{
return [NSTimer scheduledTimerWithTimeInterval:seconds
target:self
selector:#selector(timerFired:)
userInfo:[blk copy]
repeats:repeats];
}
#end
I found this code over at http://orion98mc.blogspot.ca/2012/08/objective-c-blocks-for-fun.html
Great work
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.7
target:[NSBlockOperation blockOperationWithBlock:^{ /* do this! */ }]
selector:#selector(main)
userInfo:nil
repeats:NO
];
You have a project on github that does the job !
Cocoapod BlocksKit, allow you to Blockify a bunch of Classes...
#import "NSTimer+BlocksKit.h"
[NSTimer bk_scheduledTimerWithTimeInterval:1.0 block:^(NSTimer *time) {
// your code
} repeats:YES];
Here is the Swift version of Mc.Stever's answer:
NSTimer.scheduledTimerWithTimeInterval(0.7, target: NSBlockOperation(block: {
/* do work */
}), selector: "main", userInfo: nil, repeats: false)
What you're missing is that if the block you're passing in is on the stack then copy will do exactly what the name says — it'll create a copy of the block over on the heap. You'd therefore expect no change in the behaviour of the one you passed in; nobody new is retaining it. The copy will stay alive while the original is deallocated.
(aside: if you're not using ARC you'll also want to autorelease the copy; you're meant to pass a non-owning reference as userInfo:. Otherwise the copy will never be deallocated)
try this
typedef void(^NSTimerFiredBlock)(NSTimer *timer);
#interface NSObject (BlocksAdditions)
- (void)my_callBlock:(NSTimer *)timer;
#end
#implementation NSObject (BlocksAdditions)
- (void)my_callBlock:(NSTimer *)timer {
NSTimerFiredBlock block = (id)self;
block(timer);
}
#implementation NSTimer (MyExtension)
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds
repeats:(BOOL)repeats
callback:(NSTimerFiredBlock)blk
{
blk = [[blk copy] autorelease];
return [NSTimer scheduledTimerWithTimeInterval:seconds
target:blk
selector:#selector(my_callBlock:)
userInfo:nil
repeats:repeats];
}
#end
So I am trying to set up a basic timer but I am failing miserably. Basically all I want is to start a 60 second timer when the user clicks a button, and to update a label with the time remaining(like a countdown). I created my label and button and connected them in IB. Next I created a IBAction for the button. Now when I tried to update the label based on the timer, my app screws up. Here's my code:
NSTimer *t = [NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector:#selector(updateLabelDisplay)
userInfo: nil repeats:YES];
I also have an updateLabelDisplay function that determines how many times the timer has ran and then subtracted that number from 60 and displays that number in the countdown label. Can anyone tell me what I am doing wrong?
Ok, well for starters, check this out if you haven't already: Official Apple Docs about Using Timers
Based on your description, you probably want code that looks something like this. I've made some assumptions regarding behavior, but you can suit to taste.
This example assumes that you want to hold on to a reference to the timer so that you could pause it or something. If this is not the case, you could modify the handleTimerTick method so that it takes an NSTimer* as an argument and use this for invalidating the timer once it has expired.
#interface MyController : UIViewController
{
UILabel * theLabel;
#private
NSTimer * countdownTimer;
NSUInteger remainingTicks;
}
#property (nonatomic, retain) IBOutlet UILabel * theLabel;
-(IBAction)doCountdown: (id)sender;
-(void)handleTimerTick;
-(void)updateLabel;
#end
#implementation MyController
#synthesize theLabel;
// { your own lifecycle code here.... }
-(IBAction)doCountdown: (id)sender
{
if (countdownTimer)
return;
remainingTicks = 60;
[self updateLabel];
countdownTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: self selector: #selector(handleTimerTick) userInfo: nil repeats: YES];
}
-(void)handleTimerTick
{
remainingTicks--;
[self updateLabel];
if (remainingTicks <= 0) {
[countdownTimer invalidate];
countdownTimer = nil;
}
}
-(void)updateLabel
{
theLabel.text = [[NSNumber numberWithUnsignedInt: remainingTicks] stringValue];
}
#end
It may be a little late to post a second answer to this question but I've been looking for a good place to post my own solution to this problem. In case it is of use to anyone here it is. It fires 8 times but of course this can be customised as you please. The timer deallocates itself when time is up.
I like this approach because it keeps the counter integrated with the timer.
To create an instance call something like:
SpecialKTimer *timer = [[SpecialKTimer alloc] initWithTimeInterval:0.1
andTarget:myObject
andSelector:#selector(methodInMyObjectForTimer)];
Anyway, here are the header and method files.
//Header
#import <Foundation/Foundation.h>
#interface SpecialKTimer : NSObject {
#private
NSTimer *timer;
id target;
SEL selector;
unsigned int counter;
}
- (id)initWithTimeInterval:(NSTimeInterval)seconds
andTarget:(id)t
andSelector:(SEL)s;
- (void)dealloc;
#end
//Implementation
#import "SpecialKTimer.h"
#interface SpecialKTimer()
- (void)resetCounter;
- (void)incrementCounter;
- (void)targetMethod;
#end
#implementation SpecialKTimer
- (id)initWithTimeInterval:(NSTimeInterval)seconds
andTarget:(id)t
andSelector:(SEL)s {
if ( self == [super init] ) {
[self resetCounter];
target = t;
selector = s;
timer = [NSTimer scheduledTimerWithTimeInterval:seconds
target:self
selector:#selector(targetMethod)
userInfo:nil
repeats:YES];
}
return self;
}
- (void)resetCounter {
counter = 0;
}
- (void)incrementCounter {
counter++;
}
- (void)targetMethod {
if ( counter < 8 ) {
IMP methodPointer = [target methodForSelector:selector];
methodPointer(target, selector);
[self incrementCounter];
}
else {
[timer invalidate];
[self release];
}
}
- (void)dealloc {
[super dealloc];
}
#end
I'm new with Objective-C, so there probably is a simple solution to this.
I want a number to increment, but each iteration to be show on a label. (for example, it shows 1, 2, 3, 4, 5... displayed apart by an amount of time).
I tried:
#import "testNums.h"
#implementation testNums
- (IBAction)start:(id)sender {
int i;
for(i = 0; i < 10; ++i)
{
[outputNum setIntValue:i];
sleep(1);
}
}
#end
and all it did was wait for 9 seconds (apparently frozen) and then displayed 9 in the text box.
To allow the run loop to run between messages, use an NSTimer or delayed perform. Here's the latter:
- (IBAction) start:(id)sender {
[self performSelector:#selector(updateTextFieldWithNumber:) withObject:[NSNumber numberWithInt:0] afterDelay:1.0];
}
- (void) updateTextFieldWithNumber:(NSNumber *)num {
int i = [num intValue];
[outputField setIntValue:i];
if (i < 10)
[self performSelector:#selector(updateTextFieldWithNumber:) withObject:[NSNumber numberWithInt:++i] afterDelay:1.0];
}
Here's one timer-based solution. You may find it easier to follow. You could set the text field's value from the text field:
#interface TestNums: NSObject
{
IBOutlet NSTextField *outputField;
NSTimer *timer;
int currentNumber;
}
#end
#implementation TestNums
- (IBAction) start:(id)sender {
timer = [[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateTextField:)
userInfo:nil
repeats:YES] retain];
//Set the field's value immediately to 0
currentNumber = 0;
[outputField setIntValue:currentNumber];
}
- (void) updateTextField:(NSTimer *)timer {
[outputField setIntValue:++currentNumber];
}
#end
Here's an even better (cleaner) timer-based solution, using a property. You'll need to bind the text field to the property in Interface Builder (select the field, press ⌘4, choose your object, and enter currentNumber as the key to bind to).
#interface TestNums: NSObject
{
//NOTE: No outlet this time.
NSTimer *timer;
int currentNumber;
}
#property int currentNumber;
#end
#implementation TestNums
#synthesize currentNumber;
- (IBAction) start:(id)sender {
timer = [[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateTextField:)
userInfo:nil
repeats:YES] retain];
//Set the field's value immediately to 0
self.currentNumber = 0;
}
- (void) updateTextField:(NSTimer *)timer {
self.currentNumber = ++currentNumber;
}
#end
The property-based solution has at least two advantages:
Your object doesn't need to know about the text field. (It is a model object, separate from the view object that is the text field.)
To add more text fields, you simply create and bind them in IB. You don't have to add any code to the TestNums class.
Yes, because that is what you told it to do. The graphics will not actually update until the main run loop is free to display them. You'll need to use NSTimer or some such method to do what you want.
A better question might be why you want to do this?