Unity / Objective-C: Trouble converting char* to NSString - ios

I have what should be a dead-simple piece of code that is failing for me in strange ways.
void MediaShare(char* text, char* furl) {
NSString *status = [NSString stringWithUTF8String: text];
NSString *media = [NSString stringWithUTF8String: furl];
[[SocialShare sharedInstance] mediaShare:status media:media];
text is just a line of text for Twitter sharing, and furl is just a file location string. I am getting a crash down past this function that comes down to bad data getting passed. Putting a breakpoint at the head of this function yields the following-
Image of Xcode variable monitor
The two values look fine, although not sure if the * values that only contain the first char are a problem.
Anyway, jumping to the end, and status and media appear to be converted to hex values.
Converted to hex values?
Any ideas? To give the full story, this is a Unity plug-in. I am simply passing in two strings from Unity C# to this function.
Thanks so much.

The code looks fine so far, if the input values are well formed C char* strings that can be interpreted as an UTF-8 encoded string.
status and media being hex values at the end of the function hint that they are. Both are pointers to Objective-C objects, so this is expected. Print them to the debug console or use po status at the debug console to check their contents (it will print the result of the -description method of status which is the string content in that case).
The subsequent crash might be caused elsewhere in the code.
What's the crash's log output?

Related

My NSDictionary somehow has multiple values for one key

I have been attempting to debug a issue with my code, and just came upon an odd phenomenon. Found this in the debug area when a breakpoint was triggered:
Am I correct in observing that there are multiple values for this key: #"6898173"??
What are possible causes of this? I do not set those key-value pairs using the string literal, but by getting a substring of a string retrieved and decoded from a GKSession transmission.
I still have this up in the debug area in xcode, incase theres anything else there that might help.
EDIT:
By request, here is the code that would have created one of the two strings (another was created at an earlier time):
[carForPeerID setObject:[[MultiScreenRacerCarView alloc] initWithImage:[UIImage imageNamed:#"simple-travel-car-top_view"] trackNumber:[[[NSString stringWithUTF8String:data.bytes] substringWithRange:range] intValue]] forKey:[[NSString stringWithUTF8String:[data bytes]] substringFromIndex:9]];
The string in data might look something like this:
car00.0146898173
EDIT:
Code that sends the data:
[self.currentSession sendData:[[NSString stringWithFormat:#"car%i%#%#", [(MultiScreenRacerCarView *)[carForPeerID objectForKey:peerID] trackNumber], speed, [(MultiScreenRacerCarView *)[carForPeerID objectForKey:peerID] owner]] dataUsingEncoding:NSUTF8StringEncoding] toPeers:#[(NSString *)[peersInOrder objectAtIndex:(self.myOrderNumber + 1)]] withDataMode:GKSendDataReliable error:nil];
Sorry its hard to read. Its only one line.
What you're seeing is a debugger "feechure". When you have a mutable dictionary and modify it, the debugger may not show you the correct view of the object.
To reliably display the contents of an NSMutableArray or NSMutableDictionary, switch to the console and type po carForPeerID.

iOS Conversion from a dictionary to a NSString

I have a NSMutableDictionary holding EXIF metadata from a picture.
An example:
const CFStringRef kCGImagePropertyExifExposureTime;
Instead of accessing every key individually, I just want write the whole dictionary content into a label.
When I want to write this data into the console I would just use:
NSLog(#"EXIF Dic Properties: %#",EXIFDictionary );
That works fine, but if I use:
NSString *EXIFString = [NSString stringWithFormat:(#"EXIF Properties: %#", EXIFDictionary)];
I get warnings that the result is not a string literally and if I try to use that string to set my label.text, the program crashes.
Any idea where my error is?
[NSString stringWithFormat:(#"EXIF Properties: %#", EXIFDictionary)] is not, as you may think, a method with two arguments. It's a method with one argument. That one argument is (#"EXIF Properties: %#", EXIFDictionary), which uses the comma operator and ends up returning EXIFDictionary. So in essence you have
[NSString stringWithFormat:EXIFDictionary]
which is obviously wrong. This is also why you're getting a warning. That warning tells you that the format argument is not a string literal, because using variables as format strings is a common source of bugs. But more importantly here, that argument isn't even a string at all, and so it crashes.
Remove the parentheses and everything will be fine. That will look like
[NSString stringWithFormat:#"EXIF Properties: %#", EXIFDictionary];
I get warnings that the result is not a string literally
Nah. You get a warning saying that the format string of stringWithFormat: is not a string literal. That's because you don't know how the comma operator (and a variadic function) works (that's why one should master the C language before trying to make an iOS app). Basically what you have here:
[NSString stringWithFormat:(#"EXIF Properties: %#", EXIFDictionary)]
is, due the behavior of the comma operator, is equivalent to
[NSString stringWithFormat:EXIFDictionary]
which is obviously wrong. Omit the parentheses, and it will be fine:
[NSString stringWithFormat:#"EXIF Properties: %#", EXIFDictionary]
You don't want those parentheses:
NSString *EXIFString = [NSString stringWithFormat:#"EXIF Properties: %#", EXIFDictionary];

iOS special characters, strange issue

I've got a very strange issue. I started an iOS App about three years ago (iOS-SDK 3.0), and since then went through the SDKs 4.0 and 5.0. Since 5.0 (or maybe 5.1) I suddenly started having problems with German special chars (ä ö ü ß).
Now I can't even initialize an NSString with special chars, this line:
NSString *str = #"abcäxyz";
gives the following warning:
Input conversion stopped due to an input byte that does not belong to the input codeset UTF-8
And this one:
NSLog(#"%#", strTemp);
gives:
abc
So it's stopping at the first special char. In other projects everything is fine. I can work with special chars without any problems.
Is it a configuration problem?
EDIT: Obviously it is a problem with the file encoding.
file -I myFile
is giving:
text/x-c++; charset=unknown-8bit
Trying to convert it with iconv gives me:
conversion from unknown-8bit unsupported
What happens when you use the UTF-8 codes to initialize the string? Like so:
NSString *s = [NSString stringWithFormat:#"%C", 0xc39f]; // should be ß
As far as I know you should also be able to do this, but haven't tested it:
NSString *s = [NSString stringWithUTF8String:"0xc39f"];
Try those and see what happens. There's a number of sites around that keep UTF-8 code tables for special characters, e.g. this one.
As long as your file is encoded UTF-8, #"abcäxyz" should be fine, but the explicit form of embedding a literal unicode characters is \u????.
- (void)testGermanChar
{
NSString *expected = #"abc\u00E4xyz";
NSString *actual = #"abcäxyz";
STAssertEqualObjects(expected, actual, #"the two strings should be equivalent");
}
SOLVED: Changed the file encoding in Xcode:
Click on the file you want to change the encoding of, then open the right panel (whats the name of this pane actually? any idea?) to edit the properties. There you see "Text Encoding" under "Text Settings". That is all.

NSURL withString adds gibberish

I am trying to build a NSURL using a path string. The string looks fine but when I try to put that in an NSURL it gets a bunch of gibberish in it. Here is the code I am using to build up the url:
NSString* path = [[AppSettings instance].contentRootPath stringByAppendingPathComponent: item.fileName];
NSLog(path);
item.contentPath = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(item.contentPath.absoluteString);
And here is the log:
2012-07-25 10:28:39.983 mxHub[44036:207] /Users/casey.borders/Library/Application Support/iPhone Simulator/5.0/Applications/C2C1975D-7FAB-4E6C-A091-DC389223CC57/Documents/.content/PREZISTA-PI.pdf
2012-07-25 10:28:41.654 mxHub[44036:207] /Users/casey.borders/Library/Application扡潳畬整瑓楲杮䄀䅖獳瑥慃档e獡敳䍴捡敨楗桴剕㩌猀穩佥䕦瑮祲潆䭲祥:ㅱ䀲㨰䀴8敲潭敶湅牴䙹牯敋㩹挀牵敲瑮楓敺猀瑥慍䕸瑮祲楓敺:慭䕸瑮祲楓敺猀瑥慍卸穩㩥洀硡楓敺䀀䄢䅖獳瑥慃档䥥瑮牥慮≬䄀䅖獳瑥慃档䥥瑮牥慮l湩瑩楗桴楄瑣潩慮祲:慤慴獕湩䕧据摯湩㩧污潬䱷獯祳潃癮牥楳湯:慣䉮䍥湯敶瑲摥潔湅潣楤杮:湩瑩楗桴慄慴攺据摯湩㩧攀瑸湥敤䱤杯慄慴瑓楲杮湅潣楤杮攀瑸湥敤䱤杯慄慴䄀偖慬敹䥲整䅭捣獥䱳杯开捡散獳潌䅧牲祡攀敶瑮s灟慬敹䥲整䅭捣獥䱳杯䀀䄢偖慬敹䥲整䅭捣獥䱳杯湉整湲污"噁汐祡牥瑉浥捁散獳潌䥧瑮牥慮l潬䅧牲祡䄀偖慬敹䥲整䅭捣獥䱳杯癅湥t畮扭牥晏牄灯数噤摩潥牆浡獥椀摮捩瑡摥楂牴瑡e扯敳癲摥楂牴瑡e畮扭牥晏祂整味慲獮敦牲摥渀浵敢佲卦慴汬s畤慲楴湯慗捴敨d敳浧湥獴潄湷潬摡摥畄慲楴湯瀀慬批捡卫慴瑲晏獦瑥瀀慬批捡卫獥楳湯䑉渀浵敢佲卦牥敶䅲摤敲獳桃湡敧s敳癲牥摁牤獥s剕I汰祡慢正瑓牡䑴瑡e畮扭牥晏敓浧湥獴潄湷潬摡摥开汰祡牥瑉浥捁散獳潌䕧敶瑮䀀䄢偖慬敹䥲整䅭捣獥䱳杯癅湥䥴瑮牥慮≬吀ⱤⱒN噁汐祡牥瑉浥捁散獳潌䕧敶瑮湉整湲污搀捩t┊#噁汐祡牥瑉浥牅潲䱲杯开牥潲䱲杯牁慲y灟慬敹䥲整䕭牲牯潌g≀噁汐祡牥瑉浥牅潲䱲杯湉整湲污"噁汐祡牥瑉浥牅潲䱲杯湉整湲污䄀偖慬敹䥲整䕭牲牯潌䕧敶瑮攀牲牯潃浭湥t牥潲䑲浯楡n牥潲卲慴畴䍳摯e灟慬敹䥲整䕭牲牯潌䕧敶瑮䀀䄢偖慬敹䥲整䕭牲牯潌䕧敶瑮湉整湲污"噁汐祡牥瑉浥牅潲䱲杯癅湥䥴瑮牥慮l椀剳捥牯楤杮攀牲牯楗桴潄慭湩挺摯㩥獵牥湉潦:潶捩䍥湯牴汯敬䕲摮汐祡慢正湉整牲灵楴湯:潶捩䍥湯牴汯敬䕲摮敒潣摲湉整牲灵楴湯:潶捩䍥湯牴汯敬偲慬批捡䉫晵敦䅲慶汩扡敬戺晵敦㩲瘀楯散潃瑮潲汬牥楄卤慴瑲汐祡湩㩧畳捣獥晳汵祬:upport/iPhone ㈜ǪᶀЀꀠ/Āࠀ쵠޴imulator/5.0/Applications/C2C1975D-7FAB-4E6C-A091-DC389223CC57/Documents/.content/Demo.pdf
It's to do with NSLog, nothing to do with your urls :)
Try this :
NSString* path = [[AppSettings instance].contentRootPath stringByAppendingPathComponent: item.fileName];
NSLog(#"%#", path);
item.contentPath = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSLog(#"%#", item.contentPath.absoluteString);
The first parameter for NSLog is a format string - this tells NSLog that there might be other values to insert into the output (these are the other optional parameters that NSLog takes - for example NSLog(#"%i", 5); would replace the %i with 5).
The NSLog(path); is fine because there aren't any special formatting characters in the path so it outputs as you would expect.
The second NSLog has % characters in - you added them when you asked to % escape the path :) For example, you replaced the space in 'Application Support' with %20.
This means that your url now contains the formatting code %20S (the S is the next character after the space, the first character in the word 'Support').
%S is interpreted by NSLog as 'take the next paramter that I have been given and treat it as a null terminated c-string'.
Unfortunately, you didn't give NSLog another parameter :) However, NSLog didn't know that - it just dutifully did as it was told and took the next value on the stack and added it to the output string.
The next value on the stack is garbage which is why your output is garbage!
You can see that the corrupted values appear instead of the spaces in your path - both spaces happen to be followed by an S so NSLog is dumping garbage in two places. That's why after the first load of garbage you can see 'upport/iPhone' and after the second 'imulator/5.0' :)
When you use NSLog(string), that string is treated as a format statement and any '%' characters will be interpreted.
Try printing the strings out using this form: NSLog(#"%#", string). As a general statement you should ALWAY use the above form and never the one you are using.

Using output parameters with ARC

So I have read this question, which seems to be exactly the kind of problem I am having, but the answer in that post does not solve my problem. I am attempting to write a data serialization subclass of NSMutableData. The problematic function header looks like this:
-(void)readString:(__autoreleasing NSString **)str
I do some data manipulation in the function to get the particular bytes the correspond to the next string in the data stream, and then I call this line:
*str = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
No errors in this code. But when I try to call the function like so:
+(id) deserialize:(SerializableData *)data
{
Program *newProgram = [[Program alloc] init];
[data readString:&(newProgram->programName)];
On the line where I actually call the function, I get the following error:
Passing address of non-local object to __autoreleasing parameter for write-back
I have tried placing the __autoreleasing in front of the NSString declaration, in front of the first *, and between the two *'s, but all configurations generate the error.
Did I just miss something when reading the other question, or has something in the ARC compiler changed since the time of that post?
EDIT:
It seems that the problem is coming from the way I am trying to access the string. I can work around it by doing something like this:
NSString* temp;
[data readString&(temp)];
newProgram.programName = temp;
but I would rather have direct access to the ivar
You can't. You might gain insight from LLVM's document Automatic Reference Counting, specifically section 4.3.4. "Passing to an out parameter by writeback". However, there really isn't that much extra detail other than you can't do that (specifically, this isn't listed in the "legal forms"), which you've already figured out. Though maybe you'll find the rationale interesting.

Resources