Very strange NSPredicate behavior when logging - objective-c

I'm trying to create a predicate to sort an array of objects that have a date property. However, when I try to NSLog the predicate, an exception is raised and I get a strange error.
Here's the code:
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy"];
NSDate * minDate = [dateFormatter dateFromString:#"2011"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"date >= %# ", minDate];
NSLog(predicate);
Here's the error:
2012-02-04 12:45:58.400 MyApp[42746:17303] -[NSComparisonPredicate length]: unrecognized selector sent to instance 0x9359000
So, why is this happening and how can I fix it?

try: NSLog(#"%#", predicate)
NSLog expects a NSString, that works as an format string. The number of format specifiers in the format string will specify, how many arguments need to follow.
from the doc:
NSLog
Logs an error message to the Apple System Log facility.
void NSLog (
NSString *format,
...
);
Discussion
Simply calls NSLogv, passing it a variable number of
arguments.

Related

potential leak value stored never read in NSMutableArray

NOTE: i want to alloc my NSMutableArray and then assign data in it.
i got leak in below code
NSMutableArray *responseArr=[[NSMutableArray alloc]init];
responseArr =[response valueForKey:#"result"];
Also in here value stored never read
NSString *name=_txt_name.text;
if ([name length]==0) {
name=#"";
}
Also leak in below
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/yyyy HH:mm:ss"];
//below here i get leak
NSDate *dateStart = [[NSDate alloc] init];
dateStart = [dateFormatter dateFromString:startDate];
EDIT :
I have disable ARC using -fno-objc-arc in reachability Class even i get below leaks
This line
NSMutableArray *responseArr=[[NSMutableArray alloc]init];
Allocates a new NSMutableArray and assigns a reference to it to responseArr
This line
responseArr = [response valueForKey:#"result"];
Takes a value from a dictionary (that presumably is a reference to an NSMutableArray) and assigns that to responseArr. At this point there are no more references to the original NSMutableArray that you allocated, so it will be deallocated.
You can just say:
NSMutableArray *responseArr = (NSMutableArray *)response[#"result"];
Similarly with your date code, you could just say:
NSDate *dateStart = [dateFormatter dateFromString:startDate];
There is no need to assign an initial value, only to throw it away.
None of the code you have shown will cause leaks, assuming you are using ARC.
If you aren't using ARC, then start using ARC!
A. First snippet
Using MRC (MRR) you have to balance allocations with +alloc your own:
NSMutableArray *responseArr = [[NSMutableArray alloc]init];
responseArr = [response valueForKey:#"result"];
In the first line you allocate an instance of NSArray. You have to (auto-)release it later, otherwise it is a memory leak. I think you misunderstood that you create a new instance of NSArray using -valueForKey and assign the value of this new reference to responseArr. (Therefore you do not need a mutable array.) After that you created two objects having only one reference to the new one, you can never release the old one.
Do something like this:
NSArray *responseArr = [[NSArray alloc]init];
NSArray *resultArr =[response valueForKey:#"result"];
[responseArr release];
You do not have to do that with the object referred by resultArr, because it isn't created with +alloc-init. Here you get an introduction to ownership.
B. Third snippet
It is the same with the last example:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
You +alloc-init that instance object, but there is no release in your code. Therefore it cannot be released anymore, when the reference dateFormatter loses its extent (lifetime), i. e. returning from the method.
Add this code at the end of the method:
[dateFormatter release];
C. Second snippet
The second example has a different problem: You assign a value to the var name without using the variable in the code below. So assigning the value is meaningless.

Why do I have to define the class of the pointer?

I'm studying Objc with the book from BigNerdRanch 'Objective-C Programming from Aaron Hillegass' and there's this thing that keeps puzzling me.
I understand that the complier needs to know what kind of variable I'm talking about so i have to declare the var type before assigning a value.
int myNum = 10;
Fine. But when it comes to ObjC classes, what's the reason for declaring the type of pointer if I have to declare it right after the =, when I alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
Obviously the *dateFormatter object is an istance of NSDateFormatter, I wrote it in the allocation. Why do i have to declare it at the beginning too?
'Cause if I try to do something like
NSString *timeZone = [NSTimeZone systemTimeZone];
Xcode clearly warns me with 'Incompatible pointer types initializing 'NSString *' with an expression of type 'NSTimeZone *'.
I feel like I'm missing something. Sorry if it's a dumb question, trying to learn.
Here the real question is rather "Why do I have to define the correct class of the pointer?"...
The answer is: you may want to use the variable in some other context as well. If you message [NSTimeZone systemTimeZone] directly, then the compiler may be able to deduce the type, but what if you message the variable? If you go with the weaker-typed
id tz = [NSTimeZone systemTimeZone];
then there's much less opportunity for the compiler to check for errors if you use tz where an NSTimeZone * is expected than it could if you declared it as NSTimeZone *tz.
As an even clearer example, suppose you have a method:
- (NSTimeZone *) userSpecifiedTimeZone {
id timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName;
return timeZone;
}
See the bug?
Xcode won't catch it, since it's perfectly valid to assign any object to a variable of type id, and to return an id from a method whose return type is any object type, and to subsequently assign that id to another id variable, or try to send messages to it, in the calling code.
You'll find this bug—if you don't catch it early with your own human eyes—only at run time, and only when the user enters a bogus time zone name, and you then try to use that time zone name (wrongly returned as this method's result) as an NSTimeZone object.
Compare to the statically-typed version:
- (NSTimeZone *) userSpecifiedTimeZone {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName; //Clang calls shenanigans here
return timeZone;
}
Clang rightly objects that assigning an NSString * to a variable typed as NSTimeZone * is suspicious.
You don't have to define the class of the pointer, but the potential otherwise for bugs like the one shown above is why we do it.
But when it comes to ObjC classes, what's the reason for declaring the
type of pointer if I have to declare it right after the =, when I
alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
You're not declaring the type of pointer twice. There's a lot going on in this statement. The first occurrence of 'NSDateFormatter' is telling the compiler that dataformatter is a pointer to this type of object, whereas the second occurrence of 'NSDateFormatter' is calling the 'alloc' method in the NSDateFormatter class. Same word, two completely different meanings.
The first thing that happens is [NSDateFormatter alloc] which is calling the (class) method 'alloc' in the 'NSDateFormatter' class. This returns an (empty) instance of an NSDateFormatter object in which the method 'init' is called. A pointer to the resultant object is then stored in your 'dateFormatter' variable, and we tell the compiler that this is a pointer to an NSDateFormatter object.
Think of it like this:
NSDateFormatter *dateFormatter; Create a pointer to an NSDateFormatter object.
newDate = [NSDateFormatter alloc]; Create an empty NSDateFormatter object by calling the class method alloc in NSDateFormatter
[newDate init]; Initialise it by calling the onject's 'init' method
dateformatter = *newDate; Assign a pointer to it to my variable.

NSDateFormatter and setDateFormat: and SSS

Using NSDateFormatter, here is my input :
00:20:11,026 (NSString)
And here is the output i want :
"00:20:11,026"; (NSDate)
but what I obtained is :
"1970-01-01 00:20:11 +0100" (NSDate)
// SSS is missing and there is too much information...
Here is what i coded :
...
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"hh:mm:ss,SSS"];
[dateFormatter dateFromString:#"00:20:11,026"];
...
Well, first of all, your NSDateFormatter is just fine.
Judging by the output, it looks like you're just NSLogging an actual NSDate object, and expecting the format to be representative of the NSDateFormatter that was used to create it. All NSDate objects will be formatted the same if you NSLog them, regardless of whether you got them from an NSDateFormatter or not. The "1970-01-01 00:20:11 +0100" string that you are worried about is actually supplied by NSDate's implementation of the -description method.
So my guess is that, while the fractional seconds (SSS) are missing from your log statement, they actually are present and accounted for in the NSDate object. Furthermore, the "too much information" is also just an artifact of the default description provided by NSDate. I'm fairly certain your actual NSDate contains all of the information you want it to.

NSDate's initWithString: is returning nil

I'm using Stig Brautaset's JSON framework serialize some objects, including NSDates (which are not directly supported).
I decided to use NSDate's description as the JSONFragment representation of a date (I don't care about the minor loss of precision incurred in doing so).
To extend Stig Brautaset's JSON framework to include NSDates, I defined a category:
#interface NSDate (NSDate_JSON) <JSONInitializer>
-(NSString *) JSONFragment;
#end
To recreate an NSDate (and other classes) from JSON, I defined a protocol with the following initializer:
#protocol JSONInitializer <NSObject>
-(id) initWithJSONRepresentation: (NSString *) aJSONRepresentation;
#end
I'm having issues with this initializer. In NSDate's case, it just calls initWithString:, and that's were I get into trouble: it always returns nil. This is the implementation:
#import "NSDate+JSON.h"
#implementation NSDate (NSDate_JSON)
-(NSString *) JSONFragment{
NSString *strRepr = [self description];
return [strRepr JSONFragment];
}
-(id) initWithJSONRepresentation:(NSString *)aJSONRepresentation{
return [self initWithString: aJSONRepresentation]; //returns nil!
}
#end
I'm not sure what's going on. Besides, the compiler warns me that the initWithString: method in initWithJSONRepresentation: could not be found.
Anybody knows what might be going on?
The full source code for a test case is available here.
You should always use an NSDateFormatter when attempting to convert a string into a date or vice versa. -initWithString: does exist on the Mac, but not on iOS. It requires the string to be in an extremely precise format. Using a date formatter is by far the superior solution.
And as a side note, your code would break if Apple ever decided to change the format of -[NSDate description].
Your test program is:
NSDate *d1 = [[[NSDate alloc] init] autorelease];
NSString *repr = [d1 JSONFragment];
NSDate *dd = [[[NSDate alloc] initWithString:[d1 description]] autorelease ];
NSDate *d2 = [[[NSDate alloc] initWithJSONRepresentation:repr] autorelease];
Your -JSONFragment category method is:
-(NSString *) JSONFragment{
NSString *strRepr = [self description];
return [strRepr JSONFragment];
}
What’s happening in this method is that you obtain a string representation of that date using -description, and then a JSON representation of that string using -JSONFragment.
In SBJSON, -JSONFragment returns the representation of a given object as JSON data. The JSON specification requires that strings are quoted. In your program:
NSString *repr = [d1 JSONFragment];
repr contains a string like #"\"2011-04-29 10:20:30 -0600\"". Because of the quotes, that string is not a valid string to be used with -[NSDate initWithString:].
If you change:
NSString *repr = [d1 JSONFragment];
to:
NSString *repr = [[d1 JSONFragment] JSONFragmentValue];
so that SBJSON parses that fragment and returns a string without the quotes, it should work.
The reason initWithString: can't be found is that unless you're importing Foundation and didn't show us here, your code can't see NSDate.h, so it doesn't know that initWithString: exists.
Dave's quite right about relying on description and using an NSDateFormatter instead. It doesn't seem likely that description will change, but there's no guarantee that it will continue to be valid input for initWithString: which has a strict input requirement:
A string that specifies a date and time value in the international string representation format — YYYY-MM-DD HH:MM:SS ±HHMM, where ±HHMM is a time zone offset in hours and minutes from GMT (for example, “2001-03-24 10:45:32 +0600”).
You must specify all fields of the format string, including the time zone offset, which must have a plus or minus sign prefix.
If your string differs in any way (including, as has become apparent, having quotes in it), you'll get nil.

NSDateFormatter Incompatible Type Error

I am receiving the following error in Xcode:
warning: incompatible Objective-C types 'struct NSDate *', expected 'struct NSString *' when passing argument 1 of 'setUpdate:' from distinct Objective-C type
The error happens when I am trying to save the formatted string to myObj.update
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
//myObj is an object with instance variable 'update' as a NSString string
myObj.update = [formatter dateFromString:#"2011-03-17T18:15:05Z"];
[formatter release];
I know I am doing something wrong that is minor but can't pinpoint. Thanks for the help! :)
dateFromString: returns an instance of NSDate, not a string. You need to make your update property an NSDate. The compiler is basically telling you that the method is returning a date, but you're trying to assign that to a string property, and that's just not gonna be healthy!