PDA

View Full Version : Converting NSData to NSString SIGABRTs me




Maury
Jun 18, 2012, 03:05 PM
As part of the startup routines in the ODBC code I'm working on, a background thread is fired off that runs a series of queries to build up the DB schema.

When I run this against a MySQL test database on my machine, everything works fine.

But when I run it against a Firebird DB on the same machine it pukes. The problem appears to be nothing more than a "$" character in a table name...

(lldb) po nameAsData
(NSData *) $10 = 0x00000001020d32d0 MON$ATTACHMENTS

That po is being typed in when the system is brake'd on the second of these two following lines:

NSData *nameAsData = [dictForTable valueForKey:@"table_name"];
NSString *tableName = [[[NSString alloc] initWithData:nameAsData encoding:[conn defaultEncoding]] autorelease];

When I continue, boom:

2012-06-18 15:51:11.795 ODBCQueryTool[2398:802f] -[__NSCFString bytes]: unrecognized selector sent to instance 0x1020d32d0

The error being reported also seems odd to me...



Sydde
Jun 18, 2012, 03:38 PM
Obviously Firebird is giving you a NSString rather than NSData for the key. Your code should probably check the class of the return value and proceed accordingly.

Maury
Jun 18, 2012, 07:14 PM
Yes, of course, that's why I'm using initWithData:nameAsData

This code works fine if there's no "$" in the string.

Sydde
Jun 18, 2012, 09:07 PM
Your second line of code expects nameAsData to be a NSData object. It seems to be getting a NSString object instead. This is what the error is telling you, that nameAsData is a NSString, not NSData. This is also what the po command is telling you. Where it says "(NSData *)", it is identifying the variable type, from your code's declaration, but it is printing out the description by calling the object. If nameAsData were actually a NSData object, the po would show hex bytes, not text.

So, you are getting a string when you are not expecting one. How would you deal with that (I can think of two possible ways, one as good as the other)?

chown33
Jun 18, 2012, 09:12 PM
This line makes an assumption:
NSData *nameAsData = [dictForTable valueForKey:@"table_name"];

It assumes that valueForKey: is returning an object whose actual runtime class is NSData. You should inspect the actual runtime class of the returned object, rather than just assuming it's NSData. For example:
NSString *className = NSStringFromClass([nameAsData class]);
NSLog( @"Hi, my class name is %@", className );


Objective-C doesn't have type-enforced runtime type-casting. The id type (which is what valueForKey: is declared as returning) can be cast to any type. And an id can be assigned to any other object without the compiler issuing a warning or error. But neither case (casting to another type, or assigning an id) actually affects the runtime class of the object. This is unlike Java, which will throw an exception if the actual type of an object doesn't match the "alleged" type.


Just because you believe an object is NSData doesn't mean it really is. In fact, this error message suggests there is a conflict between belief and reality:
2012-06-18 15:51:11.795 ODBCQueryTool[2398:802f] -[__NSCFString bytes]: unrecognized selector sent to instance 0x1020d32d0

This is telling you that an object whose actual class is __NSCFString (a hidden class, part of the NSString class cluster) is being sent the message bytes, but the class of which the object is an instance doesn't have a selector (i.e. no concrete method) of that name. In short, you're sending the bytes message to an NSString object.

I get the sense you don't understand key-value coding as well as you need to.