This is my code:
NSError *error = nil;
SBJsonParser *parserJson = [[SBJsonParser alloc] init];
NSDictionary *jsonObject = [parserJson objectWithString:webServiceResponse error:&error];
[parserJson release], parserJson = nil;
//Test to see if response is different from nil, if is so the parsing is ok
if(jsonObject != nil){
//Get user object
NSDictionary *userJson = [jsonObject objectForKey:#"LoginPOST2Result"];
if(userJson != nil){
self.utente = [[User alloc] init];
self.utente.userId = [userJson objectForKey:#"ID"];
}
While Json string webServiceResponse is:
{"LoginPOST2Result":
"{\"ID\":1,
\"Username\":\"Pippo\",
\"Password\":\"Pippo\",
\"Cognome\":\"Cognome1\",
\"Nome\":\"Nome1\",
\"Telefono\":\"012345678\",
\"Email\":null,
\"BackOffice\":true,
\"BordoMacchina\":false,
\"Annullato\":false,
\"Badge\":1234}"
}
The problem arise when is execute this line:
self.utente.userId = (NSInteger *) [userJson objectForKey:#"ID"];
and the error is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString objectForKey:]: unrecognized selector sent to instance 0x6861520'
The error seems to be due to the fact that the object userJson is not an NSDictionary but rather NSCFString type and therefore does not respond to the message objectForKey:.
Where am I doing wrong?
The problem is that whilst the value in the json response for the key "LoginPOST2Result" looks like a dictionary, it is actually a string as it is enclosed in quotes.
So you are sending the objectForKey: message to an NSString and not an NSDictionary. NSString does not respond to objectForKey:.
It looks like the webServiceResponse is being generated incorrectly or parsed incorrectly.
You need to better understand what is a pointer and what no in the Cocoa Framework.
Infact you are defining userJson to NSDictionary and not NSDictionary *. Consider that all objects in Cocoa are pointers. Infact check that [NSDictionary objectForKey:] returns an "id" and then you must use NSDictionary *. Using simply NSDictionary you will refer to the class.
Similar mistake is done later in the cast to (NSInteger *) but NSInteger (NSInteger is not an object, it's a basic type hesitated from long or int (depends on the platform architecture) as you can see from its definition:
#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
#else
typedef int NSInteger;
#endif
And also it seems from the object definition above that the key you are trying to get is dumped as a string and you are trying to fetch a dictionary. Please check the original json which probably is not in the format you expect.
So at the end you have at least 3 errors that will make your app crash.
Related
A bit new to Objective-C, so please bear with me.
Firstly, I'm using the FMDB library for SQLite management.
I'm populating an NSMutableDictionary using the following method:
//....
while([effectivenessResults next]) //this loops through the results of a query (verified that this works)
{
NSMutableArray *dFactors = [[NSMutableArray alloc]init];
if([resultDict objectForKey:[effectivenessResults stringForColumn:#"tName"]])
{
dFactors = [resultDict objectForKey:[effectivenessResults stringForColumn:#"tName"]];
}
NSNumber *effectivenessValToAdd = [NSNumber numberWithDouble:[effectivenessResults doubleForColumn:#"dFactor"]/100];
[dFactors addObject:[NSMutableString stringWithFormat:#"%#",effectivenessValToAdd]];
[resultDict setObject:dFactors forKey:[effectivenessResults stringForColumn:#"tName"]];
}
I'm returning the array properly (I have verified this). Then, I am accessing this NSMutableDictionary elsewhere, using the follwing method:
for(id type in tEffect) //tEffect is the NSMutableDictionary, returned from the previous code (there known as resultDict)
{
effectivenessString = [self getEffectivenessString:[tEffect objectForKey:type]];
tInfo = [NSMutableString stringWithFormat:#"%#", [tInfo stringByAppendingFormat:#"%#: %#\n", type, effectivenessString]];
}
which calls the following two methods:
-(NSMutableString *)getEffectivenessString:(NSNumber *) numberPassedIn
{
double dFactor = [numberPassedIn doubleValue];
//adds the above value to a string, this will not affect anything
}
and
-(NSNumber *) listProduct: (NSMutableArray *)listOfValues //calculates the product of an NSMutableArray of numbers
{
NSNumber *product=[NSNumber numberWithDouble:1.0];
for(int i = 0; i < [listOfValues count]; i++)
{
NSNumber *newVal = [listOfValues objectAtIndex:i];
product = [NSNumber numberWithDouble:[product doubleValue] * [newVal doubleValue]];
}
return product;
}
So, when I call these methods, I am getting the following error:
2013-08-04 13:52:04.514 effectCalculator[45573:c07] -[__NSArrayM doubleValue]:
unrecognized selector sent to instance 0x8c19e00
2013-08-04 13:52:04.521 effectCalculator[45573:c07] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[__NSArrayM doubleValue]: unrecognized
selector sent to instance 0x8c19e00'
Important to note: This error occurs on the retrieval, NOT the populating of the NSMutableDictionary. This means the population of this dictionary not the an issue, but it might have something to do with why it's having trouble retrieving the data.
So what might cause this error?
Your code is pretty difficult to follow. In future please post a minimal sample which compiles, or at least is a single block of understandable code.
Having said that, I believe your issue is with this bit:
for(id type in tEffect) //tEffect is the NSMutableDictionary, returned from the previous code (there known as resultDict)
{
effectivenessString = [self getEffectivenessString:[tEffect objectForKey:type]];
what does resultDict contain?
[resultDict setObject:dFactors ...
but dFactors is an NSMutableArray. Well getEffectivenessString expects a NSNumber, not a NSMutableArray. So it complains. Also I think you intended for the method to take a string, not a number, although I don't see why you're not casting as you load them (instead of as you use them).
Since Objective C doesn't support strongly-typed arrays or dictionaries, your best bet to defend against this in the future is to name your variables more logically. It should stand out when you try to call a method which expects a number with an array instead.
First off, my code:
#interface Block : NSObject {
NSData *data;
NSInteger slice_count;
}
#property (readonly) NSData *data;
+ (Stopwatch *) runOldTestsUsingConfiguration:(TestConfiguration *)c;
- (Slice *) getSlice:(NSUInteger)idx;
#end
- (Slice *) getSlice:(NSUInteger)idx {
void *b = (void*)[data bytes] + idx*slice_count;
int len = [data length] / slice_count;
Slice *ret = [Slice alloc];
[ret initWithBytesNoCopy:b length:len freeWhenDone:NO];
return ret;
//NSString *temp2 = [data description];
//NSRange r = NSMakeRange(idx*slice_count, [data length] / slice_count);
//NSData *d = [data subdataWithRange:r];
//NSString *temp = [d description];
//Slice *s = [[Slice alloc] initWithBytesNoCopy:(void *)[d bytes] length:r.length freeWhenDone:NO];
//return s;
}
where Slice is a simple subclass of NSData.
For some reason I'm getting a run-time error that seems to indicate my Slice instance either a) isn't actually a concrete instance (?) or b) something is going wrong in its inheritance and the message isn't binding itself to Slice properly (almost certainly by my as yet unknown error).
The exact error I'm getting is this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '***
initialization method -initWithBytes:length:copy:freeWhenDone:bytesAreVM:
cannot be sent to an abstract object of class Slice: Create a concrete instance!'
Can anyone help me out? I've tried just about everything I can think of (basic routines of which are detailed in the message call itself) and I am still coming up dry. What does it mean when it says 'create a concrete instance'? Isn't that what I'm doing when I alloc it?
Subclassing NSData is a lot more complicated than you would think. In most cases you are better off just writing a wrapper around NSData instead of a full subclass.
IIRC, init methods are allowed to re-assign self, and should therefore ALWAYS be used on the same line as alloc.
Slice *ret = [[Slice alloc] initWithBytesNoCopy:b length:len freeWhenDone:NO];
I'm not sure if that's the root cause, but it's a red-flag to me that may lead you in a good direction.
EDIT:
Actually, it has me wondering if you have overridden +alloc in your subclass and aren't returning an instance...
I have this question here (as well other quesrtions on SO), and the Apple docs about Objective-C collections and fast enumeration. What is not made clear is if an NSArray populated with different types, and a loop is created like:
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
What exactly happens here? Will the loop skip over anything that is not an NSString? For example, if (for the sake of argument) a UIView is in the array, what would happen when the loop encounters that item?
Why would you want to do that? I think that would cause buggy and unintended behavior. If your array is populated with different elements, use this instead:
for (id object in myArray) {
// Check what kind of class it is
if ([object isKindOfClass:[UIView class]]) {
// Do something
}
else {
// Handle accordingly
}
}
What you are doing in your example is effectively the same as,
for (id object in myArray) {
NSString *string = (NSString *)object;
NSLog(#"%#\n", string);
}
Just because you cast object as (NSString *) doesn't mean string will actually be pointing to an NSString object. Calling NSLog() in this way will call the - (NSString *)description method according to the NSObject protocol, which the class being referenced inside the array may or may not conform to. If it conforms, it will print that. Otherwise, it will crash.
You have to understand that a pointer in obj-c has no type information. Even if you write NSString*, it's only a compilation check. During runtime, everything is just an id.
Obj-c runtime never checks whether objects are of the given class. You can put NSNumbers into NSString pointers without problems. An error appears only when you try to call a method (send a message) which is not defined on the object.
How does fast enumeration work? It's exactly the same as:
for (NSUInteger i = 0; i < myArray.count; i++) {
NSString* string = [myArray objectAtIndex:i];
[...]
}
It's just faster because it operates on lower level.
I just tried a quick example... Here is my code.
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:1];
NSNumber *number = [NSNumber numberWithInteger:6];
[array addObject:number];
[array addObject:#"Second"];
Now if I simply log the object, no problem. The NSNumber instance is being cast as an NSString, but both methods respond to -description, so its not a problem.
for (NSString *string in array)
{
NSLog(#"%#", string);
}
However, if I attempt to log -length on NSString...
for (NSString *string in array)
{
NSLog(#"%i", string.length);
}
... it throws an NSInvalidArgumentException because NSNumber doesn't respond to the -length selector. Long story short, Objective-C gives you a lot of rope. Don't hang yourself with it.
Interesting question. The most generic syntax for fast enumeration is
for ( NSObject *obj in myArray )
NSLog( #"%#\n", obj );
I believe that by doing
for ( NSString *string in myArray )
NSLog( #"%#\n", string );
instead, you are simply casting each object as an NSString. That is, I believe the above is equivalent to
for ( NSObject *obj in myArray ) {
NSString *string = obj;
NSLog( #"%#\n", string );
}
I could not find precise mention of this in Apple's documentation for Fast Enumeration, but you can check it on an example and see what happens.
Since all NSObject's respond to isKindOfClass, you could still keep the casting to a minimum:
for(NSString *string in myArray) {
if (![string isKindOfClass:[NSString class]])
continue;
// proceed, knowing you have a valid NSString *
// ...
}
This is bizarre.
Developing for the iPhone I have an NSString:
distanceFromTargetString = #"6178266.000000"
// this is not actually code, but a value taken from elsewhere. It is a proper NSString though.
When I use this
NSArray *listItems = [distanceFromTargetString componentsSeparatedByString:#"."];
distanceFromTargetString = [listItems objectAtIndex:0];
or this
[distanceFromTarget setText: distanceFromTargetString];
I get something like this
-[NSDecimalNumber isEqualToString:]: unrecognized selector sent to instance 0x1cac40
2011-07-21 14:29:24.226 AssassinBeta[7230:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSDecimalNumber isEqualToString:]: unrecognized selector sent to instance 0x1cac40'
Any ideas?
At some point you are assigning an NSDecimalNumber to distanceFromTargetString rather than an NSString. There is no run-time type checking of assignments in Objective C, so this is totally "legal":
NSDecimalNumber *number = [NSDecimalNumber ....];
[array addObject:number];
NSString *string = [array lastObject];
The above will generate no errors or warnings until you try to send NSString methods to string, at which point you will get an exception (crash) like you show above.
Do an audit of everywhere you assign distanceFromTargetString, and everywhere you use NSDecimalNumber. Somewhere you're crossing the streams.
You could try :
NSInteger i = [distanceFromTargetString integerValue];
NSString s = [NSString stringWithFormat:#"%d", i];
// you got your string
You are somewhere calling isEqualToString with a NSDecimalNumber as the receiver. What is distanceFromTarget? Is this an NSDecimalNumber?
The first thing should work.
You could try to set a breakpoint at the line
[distanceFromTarget setText: distanceFromTargetString];
and see if distanceFromTargetString is actually an NSString.
As mentioned above, somehow an NSNumber has sneaked in somewhere.
i've written a for loop in Objective-C, This is how my code looks like
NSString *string = [NSString stringWithContentsOfFile=#"/Users/Home/myFile.doc"];
NSString *seperator=#"\n";
NSArray *mainarray = [string componentsSeparatedByString:seperator];
// Since i want to parse each element of mainarray
for(NSString *s in mainarray)
{
//again parising the string using a new separator
NSString newseparator = #"=";
NSArray *subarray = [s componentsSeparatedByString : newseparator];
//Copying the elements of array into key and object string variables
NSString *key = [subarray objectAtIndex:0];
NSLog(#"%#",key);
NSString *class_name= [subarray objectAtIndex:1];
NSLog(#"%#",class_name);
// create an instance for the class_name
//dont knw how it ll take the value from file and ???
//Putting the key and objects values into hashtable
NSMutableDictionary = [NSDictionary dictinaryWithObject:class_name forKey:key];
}
Whenever i execute this code this crashes my program saying as, Terminating the app due to uncaught exception NSRangeException
How to know the range of array and how to specify the terminating condition in the for loop???and plz let me knw how to handle this exception???
I'm surprised that code even compiles. If I remember correctly, it can't compile unless you have gone to great lengths to turn off a whole bunch of compiler warnings.
NSString newseparator = #";";
That should give an error write there in that you don't have the *.
NSString *key = [subarray objectAtIndex[0]];
NSString *object = [subarray objectAtIndex[1]];
Neither of these lines of code make any sense.
It would appear that you haven't posted the actual code?
Now, getting back to the exception. A range exception will be tossed if you try to access an item at an index that is outside of the range of indexes available in the array. Thus, if componentsSeparatedByString: returned an array of 0 or 1 elements, then [subarray objectAtIndex: 1]; will cause a range exception to be raised.
What you don't want to do is to try and handle the exception using an #catch block. In Cocoa (and iPhone development), exceptions are treated as non-recoverable errors.
So, instead, use the -count method on NSArray to verify that the array actually contains the # of elements you were expecting. Since you are writing a casual parser, this is probably a good idea as a minimal check of input validity.