I am trying to compare two NSDates one is created by the viewDidLoad method and the other by clicking a button. I want to be able to click the button and find the time difference since the viewDidLoad was ran. I keep getting a difference of nil. Any Ideas?
#import "TimeViewController.h"
id startTime;
#implementation TimeViewController
- (void)viewDidLoad {
NSDate *startTime = [NSDate date];
NSLog(#"startTime = %#",startTime);
}
- (IBAction)buttonPressed{
NSDate *now = [NSDate date];
NSLog(#"now = %#",now);
double timeInterval = [now timeIntervalSinceDate:startTime];
NSLog(#"time difference = %#",[NSString stringWithFormat:#"%g",timeInterval]);
}
You have
id startTime;
in the global scope, and also
NSDate *startTime = [NSDate date];
inside viewDidLoad. The second statement creates a local variable called startTime, which hides the global variable. Use
startTime=[[NSDate date] retain];
instead.
That said, I'd suggest you not to create the global variable. Instead, make it an instance variable and a property:
#interface TimeViewController :NSObject{
....
NSDate*startDate;
}
...
#end
and as Kubi said, don't forget
-(void)dealloc{
[startDate release];
[super dealloc];
}
I'd also suggest not to use id to hold a known object. Who told you that? That's a very bad practice. Even when you declare a global variable, you should use
NSDate*startDate;
so that the compiler can warn you against non-defined methods.
Related
I have one method that I use in many places throughout my project that looks like the following:
-(void)showSignInView
{
if(check for time interval)
[[self superview] addSubview:loginController.view];
}
I'd like to note the first time that this method is called, then on every subsequent call of this method check to make sure that the interval has been more than 15 minutes from the original call. Only then will it execute the rest of its code.
I know that you can use NSDate to measure time intervals using code like the following:
NSDate *firstTime = [[NSDate date] retain];
NSDate *SecondTime = [NSDate date];
NSLog(#"Time elapsed: %f", [SecondTime timeIntervalSinceDate:firstTime]);
but I'm not sure how to implement the initial time check, then subsequent comparisons to that time. How can I do this?
Create a property named previousTime.
#property(nonatomic, retain) NSDate *previousTime;
And create a method to find the time difference.
- (NSTimeInterval)timeDifferenceSinceLastOpen {
if (!previousTime) self.previousTime = [NSDate date];
NSDate *currentTime = [NSDate date];
NSTimeInterval timeDifference = [currentTime timeIntervalSinceDate:previousTime];
self.previousTime = currentTime;
return timeDifference;
}
You could use GCD to achieve this. The dispatch_once() function can arrange that a block is only executed once in the lifetime of your app.
NSDate *firstTime = nil;
- (void)loadView {
[self calculateTime:[NSDate dateWithTimeIntervalSince1970:1312996898]];
}
- (void)calculateTime:(NSDate*)secondTime
{
double offset = [secondTime timeIntervalSinceDate:[self getFirstTime]];
if (offset >= 900.0) {
NSLog(#"15 min gone");
}
}
- (NSDate *)getFirstTime
{
if (!firstTime) {
firstTime = [[NSDate date] retain];
}
return firstTime;
}
Did any body get this issue?
If I need an instance variable, not as a property, and initialize this variable in a method, then when I need it, it is already released. It happens for autoreleased objects. What is the reason for this?
Usually instance variable should have the whole lifetime of the class object. But it seems if the variable is local to a function, and its a autorelease object, it is released when the function exits.
MyClass.h
#interface MyClass:UIViewController {
NSDate * date;
}
MyClass.m
#implementation MyClass {
- (void) anInit {
date = [NSDate date];
}
- (void) useDate {
NSLog (#"%#", date);
// here date is already release, and get bad access.
}
}
You need to retain date.
An autoreleased object will be released when the autorelease pool is next drained. When this happens has nothing to do with the lifecycle of your object.
Your implementation should look like this:
#implementation MyClass {
- (void) anInit {
date = [[NSDate date] retain]; // or [[NSDate alloc] init]
}
- (void) useDate {
NSLog (#"%#", date);
}
- (void) dealloc {
[date release];
[super dealloc];
}
}
[NSDate date] is a Convenience Constructor and is autoreleased, you need to add a retain call. Also make sure anInit is only called once or you will create a memory leak without calling [date release] first.
- (void) anInit {
date = [[NSDate date] retain];
}
I have these functions here that will record the time between starting point and pressing a button to stop the time, then finally print the length of time recorded:
-(void)informToPress
{
textLabel.text = #"Test, press the button";
//begin record and end record on button press
startDate = [[NSDate alloc]init];
}
-(IBAction)stopTime{
stopDate = [[NSDate alloc]init];
textLabel.text = [NSString stringWithFormat:#"Time : %f", [stopTimer timeIntervalSinceDate:startTimer]];
}
But where I have:
textLabel.text = [NSString stringWithFormat:#"Time : %f", [stopTimer timeIntervalSinceDate:startTimer]];
I need this to be placed in a different View, and therefore a different .m file! How could I use this line of code in a completely different file? As the new file/view doesn't know what the values are or textLabel.
You can implement a singleton called dateManager with the properties startTimer and stopTimer.
You will create only one instance of dateManager so it will be the same value for your properties anywhere in the code.
Or you can also create in one .h file (it's easier):
static NSDate *startTimer = nil;
static NSDate *stopTimer = nil;
And give them values when you need but don't forget to include the .h file where you need the variables.
And the right way to initiate a NSDate with the current date is to do like this :
NSDate *currentDate = [NSDate date];
I would like to assign a date from one view controller to another
-(void) setCurrentDate:(NSDate newDate){
self.currentDate = newDate;
[self updateView];
}
While debugging I see the currentDate value out of scope and the application crashes with EXC_BAD_ACCESS.
Any help will be appreciated.
Besides that your setter should take NSDate by pointer (all class-type instances are passed by pointer in Objective-C), you are recursively calling the setter:
self.currentDate = foo results in [self setCurrentDate:foo] being called.
Correctly it should look e.g. like this (assuming a nonatomic, retain property):
- (void)setCurrentDate:(NSDate *)newDate {
if (currentDate != newDate) {
[currentDate release];
[newDate retain];
currentDate = newDate;
[self updateView];
}
}
Alternatively name that method different from the setter so you can use the synthesized setter:
- (void)updateDate:(NSDate *)newDate {
self.currentDate = newDate;
[self updateView];
}
You need to pass the pointer to date. Something like this:
-(void) setCurrentDate:(NSDate* newDate){
[self.currentDate release];
self.currentDate = newDate;
[self.currentDate retain];
[self updateView];
}
Of course, your currentDate class variable should also be a NSDate pointer. It will be even better if you use a property instead of a custom made setter.
Possibly, you need to retain newDate or copy it, if it's possible.
What I mean:
You create newDate
You call setCurrentDate
You release newDate
[self updateView] try to use it and fails because it is already released.
You also can try NSZombieEnabled to catch this kind of bugs.
In your method name, you use (NSDate date). You forgot to include the "*", which makes it a pointer. The correct code should be
-(void) setCurrentDate:(NSDate *newDate){ // Notice the star after NSDate
self.currentDate = newDate;
[self updateView];
}
memory management still gives me grief. This time it is an NSDate iVar that I init using
NSDate *myNSDate = [[NSDate date] firstDayOfMonth];
with a method call to
- (NSDate *)firstDayOfMonth {
NSDateComponents *tmpDateComponents = [[NSCalendar currentCalendar]
components:NSYearCalendarUnit | NSMonthCalendarUnit | NSEraCalendarUnit | NSWeekCalendarUnit | NSWeekdayOrdinalCalendarUnit
fromDate:self];
[tmpDateComponents setDay:1];
[tmpDateComponents setHour:0];
[tmpDateComponents setMinute:0];
[tmpDateComponents setSecond:0];
return [[NSCalendar currentCalendar] dateFromComponents:tmpDateComponents];
}
At the end of the init call the retain count is at 1 (Note the iVar is not defined as a property).
When I step into the viewWillAppear method the myNSDate has vanished. I tried to do an explicit retain on it, but that only lasts until I update the iVar using the above method again.
I though - ok - I add the retain to the return of the function, but that makes the leak analyser throw up an error.
What am I doing wrong?
Your method firstDayOfMonth is correct as it is given in your question. However, the return value of that method is an autoreleased date, which means that if you don't retain the return value somewhere else, it will disappear.
So you need to do something like this (assuming your ivar is named firstDayOfMonth:
- (id) init {
if (self = [super init...]) {
...
[self setFirstDayOfMonth:[[NSDate date] firstDayOfMonth]];
}
return self;
}
- (void) setFirstDayOfMonth:(NSDate *)newFirstDay {
[firstDayOfMonth release];
firstDayOfMonth = [newFirstDay retain];
}
- (void) dealloc {
[firstDayOfMonth release];
...
[super dealloc];
}
In this, you're explicitly retaining the return value (but also making sure to release the old value so you're not leaking memory). Now your date object will live until you set a new date, or the object is deallocated and the date is destroyed in the dealloc method.