I'm trying to convert NSData to NSString, and I'm getting a SIGABRT on this command
NSString* newStr = [[NSString alloc] initWithData:theData encoding:NSUTF8StringEncoding];
It appears the exception is NSInvalidArgumentException, reason: unrecognized selector sent to instance.
I inspect the NSData object in a breakpoint and it pops up with expected data. Why on earth would a simple data conversion like this go so wrong? Any thoughts?
You guys pointed me in the winning direction by questioning type of object ... I was extracting it from a dictionary and assumed it was an NSData object, but in reality it was already an NSString. Ugh. Thanks a lot!
Related
I develop in objective-c for a couple years now, but never faced something like this. I'd like to know if someone have an explanation why this is possible, look:
NSNumber *number = [NSNumber numberWithDouble:1.23413];
NSDictionary *dicTest = #{ #"number": number };
NSString *string = [dicTest valueForKey:#"number"];
NSLog(#"Is string? %d", [string isKindOfClass:[NSString class]]);
NSLog(#"Is number? %d", [string isKindOfClass:[NSNumber class]]);
The debugger says that the type of the variable is:
string __NSCFNumber * (double)1.23413 0x000060c000222ae0
How this is possible at runtime without crashing? Objective C isn't suposed to be typed?
Objective-C doesn't strongly enforce types in the way you expect.
The return type of valueForKey: is id, which means “object identifier”. The id type can reference an instance of any class and can be converted to a pointer to any specific class without any checking. You won't get an error unless you send an unrecognized message to the instance. For example, this statement:
NSLog(#"length = %ld", (long)string.length);
triggers this run-time error:
-[__NSCFNumber length]: unrecognized selector sent to instance 0x100404230
You can enforce stronger typing sometimes using “lightweight generics”. However, you first need to understand that you should not generally be using valueForKey:. The valueForKey: message is part of “key-value coding” (KVC) and is a way to access an object's properties by name at runtime. You should instead use objectForKey: on a dictionary ([dicTest objectForKey:#"number"]) or just use subscripting directly (dicTest[#"number"]).
Okay, so let's say you mend your ways and use objectForKey instead. Then, if all the values in the dictionary will be NSNumber *, you can declare your dictionary like this:
NSDictionary<NSString *, NSNumber *> *dicTest = #{ #"number": number };
Then you'll get a warning if you assume a different type for a value extracted from the dictionary:
NSString *string = [dicTest objectForKey:#"number"];
// warning: incompatible pointer types initializing 'NSString *' with an expression of type 'NSNumber * _Nullable' [-Wincompatible-pointer-types]
NSString *string = [dicTest valueForKey:#"number"];
You're essentially "casting" the result of the dictionary lookup to be an NSString. There's no check that the result of the dictionary lookup is actually an NSString.
string __NSCFNumber * (double)1.23413 0x000060c000222ae0
The debugger shows that your variable string is actually an NSNumber, not an NSString.
How this is possible at runtime without crashing?
There's nothing illegal about declaring a an object as a completely unrelated Type. You only run into trouble when you send an invalid message that the object is unable to interpret.
For example both NSNumber and NSString have the property boolValue.
#property(readonly) BOOL boolValue;
So if you send boolValue to your "NSString", the true NSNumber object will still respond to that message. No Exception.
But if you use a String-specific method/property that ONLY NSString implements, the true NSNumber object will not be able to respond. This causes the Exception.
I am getting this error:
'-[ProductionCellData objectForKey:]: unrecognized selector sent to instance 0x14dca5d0'
on this line of code:
NSString *productionItem = [[myArray objectAtIndex:keyCounter] objectForKey:#"baseLineStart"];
keyCounter is equal to 0 and here is what myArray looks like
I am trying to get the value of baseLineStart but I keep getting the error above....how do I fix this?
objectForKey is normally used in dictionaries.
If you want to access the property, just refer to it:
NSString *productionItem = [myArray objectAtIndex:keyCounter].baseLineStart;
Or with even more modern syntax:
NSString *productionItem = myArray[keyCounter].baseLineStart;
The fact is that ProductionCellData does note responds to this selector, you should use
- (nullable id)valueForKey:(NSString *)key;
or get property from object in this way (if it is public )
NSString *productionItem = [[myArray objectAtIndex:keyCounter] baseLineStart];
Try to find error by the following way.
NSDictionary *dic = [myArray objectAtIndex:keyCounter];
NSLog(#"dic:%#",dic);
NSString *productionItem = [dic objectForKey:#"baseLineStart"];
//1. observe the dic object has "baseLineStart" key. if not found write the correct one.
//2. objeserve what type of value returns "[myArray objectAtIndex:keyCounter]". it may not NSDictionary which may resoan of crash
Looking this [__NSArrayI allKeys]: unrecognized selector sent to instance error up, it seemingly occurs when you send an NSArray the allKeys message which is meant for NSDictionary, but in this case I'm very clearly sending it to an NSDictionary.
Here's the code I use when interfacing with the Pocket API:
NSDictionary *articles = [response objectForKey:#"list"];
// Create an array we can use to sort the keys (and thus the articles) in order of when they were added
NSMutableArray *allKeys = [[articles allKeys] mutableCopy];
The last line there causes the error. But articles is very clearly declared as an NSDictionary? Why is it not liking it?
Oddly enough, if I inspect it at runtime it says it's an NSArray! Why did it change?
(lldb) po articles
$5 = 0x082103e0 <__NSArrayI 0x82103e0>(
)
(lldb) po [articles class]
$6 = 0x01b83b8c __NSArrayI
(lldb)
It may be declared in your code as a dictionary, but that doesn't make it a dictionary. It is truly an array and that is why you get the exception. Check your response so you know what you should expect.
Your code compiles because the compiler doesn't know that it's going to be an array and it trusts you that it will be a dictionary. It does this because objectForKey: returns id.
I'm trying to parse some JSON to a NSArray but I'm getting the following error:
[__NSCFDictionary length]: unrecognized selector sent to instance 0x6d7a160
The area of code that's throwing this error is:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSError* error;
NSLog(responseString);
NSArray *jsonArray = [NSJSONSerialization
JSONObjectWithData:responseData
options:NSJSONReadingMutableContainers
error:&error];
parties2=jsonArray;
NSLog([parties2 objectAtIndex:0]); //Exception thrown
[tableView reloadData];
}
parties2 is previously defined as:
parties2=[NSArray arrayWithObjects:nil];
My ResponseString looks like
[{"Name":"party 1.1","GreekName":"FoA 1","GreekID":325,"schoolName":"FoA University","schoolID":10,"numberAttending":0,"maxNumberAttending":1000,"price":15.0},{"Name":"party 1.2","GreekName":"FoA 1","GreekID":325,"schoolName":"FoA University","schoolID":10,"numberAttending":0,"maxNumberAttending":300,"price":20.0},{"Name":"party 1.3","GreekName":"FoA 1","GreekID":325,"schoolName":"FoA University","schoolID":10,"numberAttending":0,"maxNumberAttending":5000,"price":25.0},{"Name":"party 2.1","GreekName":"FoA 2","GreekID":326,"schoolName":"FoA University","schoolID":10,"numberAttending":0,"maxNumberAttending":500,"price":25.0},{"Name":"party 2.2","GreekName":"FoA 2","GreekID":326,"schoolName":"FoA University","schoolID":10,"numberAttending":0,"maxNumberAttending":300,"price":30.0},{"Name":"party 3.1","GreekName":"FoA 3","GreekID":327,"schoolName":"FoA University","schoolID":10,"numberAttending":0,"maxNumberAttending":0,"price":50.0},{"Name":"party 5.1","GreekName":"FoA 5 ","GreekID":329,"schoolName":"FoA University","schoolID":10,"numberAttending":0,"maxNumberAttending":300,"price":15.75}]
This is my first venture with ObjC, I'm coming from a .Net C# background so chances are I'm missing a very simple thing.
Thanks :)
Your JSON parsing is correct. The error is being thrown because of a bad syntax in your NSLog() statement.
NSLog() takes an NSString as an argument, but your are trying to pass an NSDictionary instead (i.e. the first element of your NSArray), which is causing the error.
The solution to this is to use an NSString format string to convert the NSDictionary object to a string, like this:
NSLog(#"%#",[parties2 objectAtIndex:0]);
I'm using JSONKit to parse a JSON string returned from my server in my iPhone application. Part of the server response is a couple of Base64 encoded images that I want to decode into actual images and add to the object that the parser creates. The problem is I can't seem to figure out what kind of class the parser returns, and thus what kind of methods that can be used to interact with the object. I've searched the JSONKit documentation for answers to my question, but haven't found it.
decodedData = [[request responseString] objectFromJSONString];
int i = 0;
[Base64 initialize];
for (NSString *base64String in [decodedData valueForKey:#"base64String"]) {
UIImage *image = [UIImage imageWithData:[Base64 decode:base64String]];
[decodedData setValue:image forKey:#"image"];
i++;
}
This code is placed in a method that gets called when the request has successfully finished and the response is returned in [request responseString] (the JSON). The decodedData object's class is defined in the header file. No matter what I declare it as (either NSArray, NSMutableArray, NSDictionary, or NSMutableDictionary) I get the same error when the code is run (it compiles just fine), which is:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '*** -[JKDictionary setObject:forKey:]: mutating method sent to immutable object'
Can anybody tell me what kind of class the object is, and what I should do to add the Base64-decoded image to the object?
I'm pretty new to Objective-C, so bear over with me. Thanks.
You're trying to mutate the immutable JKDictionary (itself a proxy for an immutable NSDictionary object).
The documentation you linked to specifies an instance method on NSString named - (id)mutableObjectFromJSONString;, this will give you a mutable dictionary object that you can play with if you so desire.