I'm so fedup with NSDate string object.
currently I am generating an unique id on the bases of NSDate as follows:
NSDate *current_date = [[NSDate date]retain];
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"HHmmssddMMYY"];
NSString *unique_id=[df stringFromDate:current_date];
NSString * current_Test_id=[NSString stringWithString:unique_id];
NSLog(#"current_test_idString %#",current_Test_id);
The code above is generating unique id and prints successfully but if I am printing or accessing currtent_Test_id in another IBAction method then app crashes.
stringWithString will create an autorelease string, modify your code as
NSString * current_Test_id = [[NSString stringWithString:unique_id]retain];
Use this Method
- (NSString *)stringDateFromDate: (NSDate *) date{
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"HHmmssddMMYY"];
NSString *current_Test_id=[NSString stringWithString:[df stringFromDate:date]];
[df release];
NSLog(#"current_tst_id %#",current_Test_id);
return current_Test_id;
}
Call Method Like that
NSString *current_tst_id = [self stringDateFromDate:[NSDate date]];
an NSString (or any object, for that matter) created with a class method, and not an init method, will be autoreleased. This means on the next iteration of the event loop, current_Test_id is released, and now you have a pointer to a dead object.
See this similar question
As current_Test_id is instance method.
in the init (in case of mac os) or viewDidLoad (for ios) alloc+init it.
and then assign :
current_Test_id=[NSString stringWithString:unique_id]; //it will be in autorelease mode.
or
current_Test_id=[[NSString stringWithString:unique_id]retain];
Related
What is the best practice for testing NSDateFormatter methods? For example, lets say I have a method:
- (NSString *)formatStringFromDate:(NSDate *)date {
NSDateFormatter *f = [[NSDateFormatter alloc] init];
[f setTimeStyle:NSDateFormatterShortStyle];
[f setDateStyle:NSDateFormatterNoStyle];
return [f stringFromDate:date];
}
There are two ways I can think of testing this method using Kiwi:
1) Create the same formatter in the unit test:
it(#"should format a date", ^{
NSDate *date = [NSDate date];
NSDateFormatter *f = [[NSDateFormatter alloc] init];
[f setTimeStyle:NSDateFormatterShortStyle];
[f setDateStyle:NSDateFormatterNoStyle];
[[[testObject formatStringFromDate:date] should] equal:[f stringFromDate:date]];
});
2) Explicitly write the intended output:
it(#"should format a date", ^{
NSDate *date = [NSDate dateWithTimeIntervalSince1970:1385546122];
NSDateFormatter *f = [[NSDateFormatter alloc] init];
[f setTimeStyle:NSDateFormatterShortStyle];
[f setDateStyle:NSDateFormatterNoStyle];
[[[testObject formatStringFromDate:date] should] equal:#"9:55 am"];
});
Now, to me, #1 seems a bit redundant. I know the test will pass as I'm essentially duplicating the method in my unit test.
Method #2 is a non-starter, as it is incredibly fragile. It completely relies on the test devices current locale being what you'd expect.
So my question is: is there a more appropriate method to test this method, or should I just go ahead with test method #1.
As you say in your comment, what you want to test is that the cell's detailTextLabel's text is set to date with the expected format when a date is present.
This can be tested in several different ways, I expose here the one I would go for.
First of all, creating a date formatter each time we want to format a date is not efficient and it makes it more difficult to test.
So what I suggest is to create a date formatter property in your table view controller:
// .h
#property (strong, nonatomic) NSDateFormatter *dateFormatter;
// .m
- (NSDateFormatter *) dateFormatter
{
if (_dateFormatter == nil)
{
_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setTimeStyle:NSDateFormatterShortStyle];
[_dateFormatter setDateStyle:NSDateFormatterNoStyle];
}
return _dateFormatter;
}
For the date formatter I would create a simple test that verifies the timeStyle and dateStyle. I am not familiar with Kiwi so I use OCUnit assertions:
TableViewController *sut;// Instantiate the table view controller
STAssertTrue(sut.dateFormatter.timeStyle == NSDateFormatterShortStyle, nil);
STAssertTrue(sut.dateFormatter.dateStyle == setDateStyle:NSDateFormatterNoStyle, nil);
After having this, the idea is to create a test that verifies that the cell returned by tableView:cellForRowAtIndexPath: has its detailTextLabel's text set with the string returned by the formatter. As you said testing the string returned by the date formatter is fragile so we can mock it, stub stringFromDate: returning a constant and verify that the detailTextLabel's text is set to that constant.
So we write the test. I'd try to write it with Kiwi mocks, so sorry if I do something wrong - the idea is the important thing:
TableViewController *sut;// Instantiate the table view controller
id mockDateFormatter = [NSDateFormatter mock];
NSString * const kFormattedDate = #"formattedDate";
NSDate * const date = [NSDate date];
[mockDateFormatter stub:#selector(stringFromDate:) andReturn:kFormattedDate withArguments:date,nil];
sut.dateFormatter = mockDateFormatter;
sut.dates = #[date];// As an example we have an array of dates to show. In the real case we would have an array of the objects you want to show in the table view.
[sut view];// In case we have the registered cells...
UITableViewCell *cell = [sut tableView:sut.tableView
cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
STAssertEqualObjects(cell.detailTextLabel.text, kFormattedDate, nil);
And the method to satisfy that test would be something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// I assume you have a standard cell registered in your table view
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
NSDate *date = [self.dates objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [self.dateFormatter stringFromDate:date];
return cell;
}
Hope it helps.
I have an NSDate category with following method
#implementation NSDate (DateUtility)
+(NSString *)dateTimeStringForDB {
NSDateFormatter *dateFormatForDB = [[NSDateFormatter alloc] init];
[dateFormatForDB setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *aDateStr= [dateFormatForDB stringFromDate:self];
[dateFormatForDB release];
return aDateStr;
}
#end
with this definition I receive a warning .
Incompatible pointer type sending 'Class' to parameter of type 'NSDate *'
However type casting self before assign it as argument suppresses this warning.
+(NSString *)dateTimeStringForDB
{
NSDateFormatter *dateFormatForDB = [[NSDateFormatter alloc] init];
[dateFormatForDB setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *aDateStr= [dateFormatForDB stringFromDate:(NSDate*)self];
[dateFormatForDB release];
return aDateStr;
}
Can we really not pass self as an argument in a category without typecasting it ? What is this feature dependent on , the compiler ? Looking for an answer before actually posting it as a question on SO I came across this, however I am still not clear as to what goes behind the scene.
You have created a class method, I suspect you really want an instance method. I assume you want to convert an instance of an NSDate (ie an object) into an NSString representation. Currently you are trying to convert the actual NSDate class into an NSString representation.
Change
+(NSString *)dateTimeStringForDB {
to
-(NSString *)dateTimeStringForDB {
The function below (i.e. dateChanged()) is triggered by a UIDatePickersolve . My problem is that
NSLog(#"Future: %#", futureDate);
returns 'null'. However,
NSLog(#"Today: %#", today);
works just fine.
I know that casting sender as a UIDatePicker, allows me to solve the problem using:
futureDate = [dateFormat stringFromDate:sender.date];
but I cannot understand why I cannot cast sender as an NSDate. Any insight would be much appreciated.
//- (IBAction)dateChanged:(UIDatePicker*)sender {
- (IBAction)dateChanged:(NSDate *)sender {
NSDate* todaysDate = [NSDate date];
NSDateFormatter* dateFormat;
dateFormat=[[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMMM d, yyyy hh:mm:ssa"];
NSString * today;
NSString * futureDate;
today=[dateFormat stringFromDate:todaysDate];
NSLog(#"Today: %#", today);
futureDate = [dateFormat stringFromDate:sender];
NSLog(#"Future: %#", futureDate);
}
Sure you can cast sender to be NSDate*. Or UIButton* - or whatever else. However it does't change the fact that date picker implementation sends an UIDatePicker* as a parameter of delegate message and the casting will be invalid. The most flexible delegate messages have id as a type of returned parameter, but the object passed is always an object of a certain class. And the Objective-c casting only makes your debugging and dev process easier with code completion and warnings for the casted class.
That would need to be casted as a UIDatePicker not NSDate. Also, it is easy to change the passed in object from type id to the native type in Interface Builder. This makes the connection properly in both the interface and implementation files. I do this occasionally when using custom subclasses of objects like UIButtons and such.
- (IBAction) dateChanged:(UIDatePicker *)sender {
NSDate* todaysDate = [NSDate date];
NSDateFormatter* dateFormat;
dateFormat=[[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MMMM d, yyyy hh:mm:ssa"];
NSString * today;
NSString * futureDate;
today=[dateFormat stringFromDate:todaysDate];
NSLog(#"Today: %#", today);
futureDate = [dateFormat stringFromDate:sender.date];
NSLog(#"Future: %#", futureDate);
}
I copied this code from another post. I tried the example, however, I am getting a EXEC_BAD_ACCESS. From what I have read, this error happens when trying to use an object that has been deallocated, but I just don't see where I am doing that:
The call
...
float weighted_average = num_of_passes / total_of_all_passes;
NSString *newNumber = [[NSString alloc] init];
newNumber = [self formattedStringWithDecimal:weightedAverage]; //weighted average (float) = 15.875145
...
The Function
- (NSString *)formattedStringWithDecimal:(NSDecimalNumber *)decimalNumber
{
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:2]; //two deimal spaces
[formatter setRoundingMode: NSNumberFormatterRoundHalfUp]; //round up
NSString *result =[NSString stringWithString:[formatter stringFromNumber:decimalNumber]];
[formatter release];
return result;
}
Call the method like this:
newNumber = [self formattedStringWithDecimal:[NSDecimalNumber numberWithFloat:15.434]];
You've tried to pass a primitive, but the method expects an object: an NSDecimalNumber. You've got to use the static convenience method numberWithFloat to create an object of that type.
And by the way, I have the feeling that
newNumber = [NSString stringWithFormat#"%.2f", 15.434];
could achieve the same result with less lines of code. Note this will not round up your number though.
You are returning an autoreleased object. Does the function that uses it retain it? If not, it could be released and then later (later run loop) its trying to be (re)used. Agreed on the enabling zombies to spot that kind of thing.
I have a function that returns an array to hold date info.
- (NSArray*) getTodayArray
{
NSDate *today = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"YYYY"];
NSString *year = [dateFormatter stringFromDate:today];
[dateFormatter setDateFormat:#"MM"];
NSString *month = [dateFormatter stringFromDate:today];
[dateFormatter release];
NSArray *res = [NSArray arrayWithObjects: year, month, nil];
return res;
}
Q1 : Is there any easy way to get all the info (year, month, date, hour, minute ...) in an array not using setDateFormat over and over again?
Q2 : Is there a way so that I can access the content of array using res['year'] or similar? I mean using dictionary?
Q3 : Do I need to release NSArray *res in the caller of this function?
A1: You can do smth like this:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"YYYY|MM"];
NSArray* d_arr = [[dateFormatter stringFromDate: [NSDate date]] componentsSeparatedByString: #"|"];
A2: Use NSDictionary:
[NSDictionary dictionaryWithObjectsAndKeys: [d_arr objectAtIndex: 0], #"year", [d_arr objectAtIndex: 1], #"month", nil]
A3: return value is autoreleased. you don't need to release it.
#prosseek
1 - I dont think you have another choice to get the year, month, date, hour, minute ... from NSDate other than this.(I am not sure about it though.)
2 - you can access the objects in the dictionary in the above format but something more like objective-c style. like this
[dateDictionary obectForKey:#"year"];
but you need to define the dictionary in that format
like this
NSDictionary *dateDictionary = [NSDictionary dictionaryWithObjects:year,min,hr,nil forKeys:#"year", #"min", #"hour", nil];
3 - no you dont need to release or autorelease the NSArray in the above method . but i think you need to retain it in the array that is receiving res array if you want to use it after a while.
Why don't you just use a NSArray of NSDates?
You can probably get all of your desired functionality out of its plethora of functions.
A1: You could dump it all out into a string, but then you'd have to parse the string, which wouldn't be any easier.
A2: You could do that if you used an NSDictionary instead of an NSArray.*
A3: No, it's already autoreleased.
* Why don't you write a category for NSDate instead?
NSDate+Convenience.h
#interface NSDate (Convenience)
- (NSInteger)year;
- (NSInteger)month;
#end
NSDate+Convenience.m
#implementation NSDate (Convenience)
- (NSInteger)year {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"YYYY"];
NSString *myYear = [dateFormatter stringFromDate:self];
[dateFormatter release];
return myYear;
}
- (NSInteger)month {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MM"];
NSString *myMonth = [dateFormatter stringFromDate:self];
[dateFormatter release];
return myMonth;
}
#end
Just #include NSDate+Convenience.h wherever you want to use your handy date and month accessors. All of your NSDate instances will then get them:
NSDate *myDate = [NSDate date];
NSLog(#"%ld %ld", [myDate year], [myDate month]);
No need for loosely-typed NSArrays or NSDictionaries to store this stuff.
(Note you could modify the above code to use a shared NSDateFormatter.)
Q1: Not an array, but you can use -[NSCalendar components:fromDate:] to get an NSDateComponents object. You can use it directly or build an array from it, if that is your preference.
Q2: No, but if you return an NSDateComponents object, then you can use -year, -month, etc methods on it.
Q3: No, you don't need to release it in this method or the caller, unless the caller retains it (which may be desirable).
You're looking for the NSDateComponents class. You'll need to create an NSCalendar object first, then call the components:fromDate: method to get the DateComponents object, after which you can access the object's month, year etc. properties.
Not quite sure what you want here. As it stands, the array cannot be accessed in the manner you describe, though if you want you could always create a dictionary and assign values for keys such as 'month' or 'year'. However, it might just be easier to return the DateComponents object, and access its properties.
No, there is no need to release the NSArray. You constructed it using the NSArray class method, which is already autoreleased.