Objective C instance variable initialization in a method - iphone

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];
}

Related

representing time interval in objective-C

I know that there is a data type called NSTimeInterval, but this is in seconds. I want to have an object representation that would be able to represent a time range, say Thursday 21 June 8:00 - Thursday 21 June 9:00. Later on I want to compare the current date/time and check whether it fits within this range. What is the best way to do this?
I would suggest using two NSDate objects to store the start and end dates. You can easily determine if a date is between them using the timeIntervalSinceDate: method:
- (BOOL)dateInInterval:(NSDate *)testDate {
// date1 is the instance variable containing the starting date
// date2 is the instance variable containing the ending date
return ([testDate timeIntervalSinceDate:date1] > 0 &&
[testDate timeIntervalSinceDate:date2] < 0);
}
You just need to make a class which holds two NSDate objects, making sure the first is before the second, and including this method.
FYI, NSTimeInterval is not a class, its a typedef of double.
Edit
Since you want use these as keys for a dictionary, you could use something similar to this to store and search your data:
#protocol IntervalDictionaryKey <NSObject>
// The class you use as keys for your dictionary must implement this method to determine if a object is in the interval
- (BOOL)intervalContains:(id)object;
#end
#interface IntervalDictionary : NSObject {
NSMutableArray *keys, *values;
}
- (void)addInterval:(id<IntervalDictionaryKey>)interval withObject:(id)object;
- (void)setObject:(id)object forIntervalOf:(id)intervalObject;
- (id)objectForIntervalOf:(id)object;
#end
#implementation IntervalDictionary
- (id)init {
if(self = [super init]) {
keys = [NSMutableArray new];
values = [NSMutableArray new];
}
return self;
}
- (void)dealloc {
[keys release];
[values release];
[super dealloc];
}
- (void)addInterval:(id<IntervalDictionaryKey>)interval withObject:(id)object {
[keys addObject:interval];
[values addObject:object];
}
- (void)setObject:(id)object forIntervalOf:(id)intervalObject {
id<IntervalDictionaryKey> key;
NSUInteger i = 0;
for(key in keys) {
if([key intervalContains:intervalObject]) {
[values replaceObjectAtIndex:i withObject:object];
break;
}
++i;
}
}
- (id)objectForIntervalOf:(id)object {
id<IntervalDictionaryKey> key;
NSUInteger i = 0;
for(key in keys) {
if([key intervalContains:object]) {
return [values objectAtIndex:i];
}
++i;
}
}
#end
Usage:
Example interval class:
#interface DateInterval : NSObject <IntervalDictionaryKey> {
NSDate *date1, *date2;
}
- (BOOL)intervalContains:(NSDate *)date; // this is the same as the dateInInterval method above
#end
#implementation DateInterval
// initializer which sets date1 and date2
- (BOOL)intervalContains:(NSDate *)date {
return ([date timeIntervalSinceDate:date1] > 0 &&
[date timeIntervalSinceDate:date2] < 0);
}
#end
Example usage code:
//intervalX is a DateInterval object, created elsewhere
//objectX is any object, created elsewhere
//objectInX is a NSDate which is part of intervalX, created elsewhere
IntervalDictionary *dict = [IntervalDictionary new];
[dict addInterval:interval0 withObject:object0];
[dict addInterval:interval1 withObject:object1];
[dict objectForIntervalOf:objectIn0]; // returns object0
[dict objectForIntervalOf:objectIn1]; // returns object1
[dict setObject:object2 forIntervalOf:objectIn1]; // changes the object for interval1 to object2
[dict objectForIntervalOf:objectIn1]; // now returns object2
NSDateComponents can be used to store components of time intervals as well as date components. You can add such an object to an NSDate using NSCalendar's dateByAddingComponents:toDate:options:.
From iOS10+ you have NSDateInterval (See https://developer.apple.com/documentation/foundation/nsdateinterval?language=objc).
Is the best data structure to represent a time interval.

NSDate timeInterval = nil

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.

NSDate assign problem

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];
}

NSDate & Memory management

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.

Please help me in solving retain mystery in iPhone?

Please consider the following code:
//CallerClass.h
#interface CallerClass : UITableViewController {
NSMutableArray *dataArray;
}
#property(nonatomic,retain) NSMutableArray *dataArray;
-(void) setData;
//CallerClass.m
#implementation CallerClass
#synthesize dataArray;
-(id)initWithStyle:(UITableViewStyle)style {
if (self = [super initWithStyle:style]) {
[self setData];
}
return self;
}
-(void) setData
{
dataArray = [CalledClass getData];
[dataArray release];
}
//CalledClass.h
#interface CalledClass : NSObject {
}
+(NSMutableArray*) getData;
//CalledClass.m
#implementation CalledClass
+(NSMutableArray*) getData
{
NSMutableArray* tempArray = [[NSMutableArray alloc] init];
return tempArray;
}
I want to know what is the retain count for dataArray that is tempArray. Is it getting released. I dont want to use autorelease as I dont know till what time period I will need it. So I want to release it on my own. When I allocated tempArray, its retain count becomes 1. But when I assign it to instance variable dataArray whose property is retain, Is the retain count for that array becomes 2 or it stays 1. Like on releasing dataArray will it release memory.
You've set up your property to retain the value, but then you're not using the accessor methods but set the instance variable directly instead:
dataArray = [CalledClass getData];
won't manage the retain count for you. You have to use:
self.dataArray = [CalledClass getData];
Also, in your CalledClass, I'd change the getData method to this:
+(NSMutableArray*) getData
{
NSMutableArray* tempArray = [[NSMutableArray alloc] init];
return [tempArray autorelease];
}
Normally I'd expect to get an autoreleased object back from a method with a name like this.
setData: should then be something like:
-(void) setData
{
self.dataArray = [CalledClass getData];
}
or you could get rid of it entirely and just directly do
self.dataArray = [CalledClass getData]
in initWithStyle:.
By calling self.dataArray instead of assigning directly to the instance variable, your dataArray property will take care of retaining and releasing the object (because you specified "retain" in the property declaration)
dataArray = [CalledClass getData];
That doesn't invoke the retain attribute of the property. That's just plain old assignment iirc. [self setDataArray:[CalledClass getData]] would give a reference count of 2 on your array.