My NSDictionary somehow has multiple values for one key - ios

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.

Related

Need help understanding a conditional crash when accessing my NSDictionary

I am keeping my data in a property called practiceRecords (an NSArray of dictionaries).
I check to see if the data already exists in the documents folder.
If yes, I load the data into self.practiceRecords.
If not, I build the array of dictionaries (using literal syntax), keeping this data in the self.practiceRecords property, and then write the data out to the documents folder.
(I am NOT reloading the data after writing it out)
As far as I am able to tell, there are no problems occurring during this process.
Then I have a step where I modify my data as follows ...
-(void)incNumberOfTriesFor:(NSString *)stringOfIndex {
if (self.practiceRecords)
{
int index = [stringOfIndex intValue];
int numberOfTries = [(NSNumber *)(self.practiceRecords[index][#"tries"]) intValue] + 1;
//CRASHING on this next line.
self.practiceRecords[index][#"tries"] = #(numberOfTries);
//message to helper method
[self writePracticeRecords];
}
}
So the first time through (when the array is built and written out) I get a crash at the indicated line.
The error is:
-[__NSDictionaryI setObject:forKeyedSubscript:]: unrecognized selector sent to instance
I quit the app, check the documents folder and see the data file written out with no issues.
I re-run the app, and then get no crash and the data file still looks great.
This is repeatable.
If the data file exists, no crash.
If the data first needs to be created, then a crash.
(In all cases, I manually look inside the resulting data file and see exactly what I expect to see - no issues there)
I'm not sure where to even begin squashing this bug, and would really like to understand the details of why this is happening.
Thanks very much for any help!
Just to recap the correct comments above:
-[__NSDictionaryI setObject:forKeyedSubscript:]: unrecognized selector sent to instance
NSDictionary does not implement any of the set... methods because it is immutable. You state that you're creating with literals syntax when the data is not found on disk. The literal syntax creates immutable containers
Instead, try...
// try to initialize from disk, but if not
// we can still use literal (immutable) syntax, but in a mutable container
self.practiceRecords = [NSMutableDictionary
dictionaryWithDictionary:#{ #"key" : #"value" }];

Am I misusing NSString/NSMutableString with my Core Data model?

I'm looping through the results of a fetch request against a Core Data store. For each object in the result list, I am reading several attributes of type string and concatenating them into one string (to be output as a CSV format file).
One particular string of one particular record in my dataset is giving me trouble: extraneous characters (kanji, arabic, etc.) are appended to the end of the string, it does not append properly to my result string, and my CSV file format is hosed.
Here is my code for looping through the fetch results and appending the string:
NSMutableString *reportString = [[NSMutableString stringWithFormat:#"...\n"];
for (int s = 0; s < [frc.sections count]; s++) {
for (int r = 0; r < [[frc.sections objectAtIndex:s] numberOfObjects]; r++) {
NSIndexPath *i = [NSIndexPath indexPathForRow:r inSection:s];
Thread *thread = [frc objectAtIndexPath:i];
NSMutableString *activity = [NSMutableString stringWithString:[thread activity]];
.
.
.
[reportString appendString:activity];
[reportString appendFormat:#",%#\n", client];
}
}
I'm using stringWithString here, but I've also used several other string methods with similar, corrupted, results. One time, Arabic letters appeared. Another time, it was "...random...FSO_CARRIER_ATT#2X.png". I've also tried using a separate fetch results array (instead of a fetched results controller).
One weird thing is that when I do a PO from lldb, the string shows up correctly. This may explain why this "corruption" doesn't show up on my table views, just when I am trying to mash strings together.
My Question
Am I copying the string values from the Core Data model incorrectly and causing this to happen? Is there a technique I am missing out on?
Update:
A screenshot of the watched variable versus an NSLog of the value:
As I mentioned in the comments, this turns out to have been a string encoding/decoding issue.
I formulated a more directed question (How do I properly encode Unicode characters in my NSString?) and got the answer very quickly.
Basically, I was using the proper string manipulation methods, as everyone here agreed. In the end, the encoding parameter I was using to attach the CSV file to the email was incorrect. At the same time, the discrepancy between the variables pane and the lldb pane in Xcode appears to be a legitimate bug. I'll be filing a bug report for that one.
I would put this whole iteration code into one serial background thread. The corruption of the order of your input has certainly to do with the threads competing against each other. You can still use separate threads, but you have to make sure they are executed serially, i.e. that one thread waits for previously scheduled threads to finish.
In Grand Central Dispatch, this would be a thread like
dispatch_queue_t serialThread =
dispatch_queue_create("serialThread", DISPATCH_QUEUE_SERIAL);
You could also use the managed object context with
[context performBlockAndWait:...];

why not EXC_BAD_ACCESS?

I've written the following code:
NSString *string = [[NSString alloc] initWithFormat:#"test"];
[string release];
NSLog(#"string lenght = %d", [string length]);
//Why I don't get EXC_BAD_ACCESS at this point?
I should, it should be released. The retainCount should be 0 after last release, so why is it not?
P.S.
I am using latest XCode.
Update:
NSString *string = [[NSString alloc] initWithFormat:#"test"];
NSLog(#"retainCount before = %d", [string retainCount]);// => 1
[string release];
NSLog(#"retainCount after = %d", [string retainCount]);// => 1 Why!?
In this case, the frameworks are likely returning the literal #"test" from NSString *string = [[NSString alloc] initWithFormat:#"test"];. That is, it determines the literal may be reused, and reuses it in this context. After all, the input matches the output.
However, you should not rely on these internal optimizations in your programs -- just stick with the reference counting rules and well-defined behavior.
Update
David's comment caused me to look into this. On the system I tested, NSString *string = [[NSString alloc] initWithFormat:#"test"]; returns a new object. Your program messages an object which should have been released, and is not eligible for the immortal string status.
Your program still falls into undefined territory, and happens to appear to give the correct results in some cases only as an artifact of implementation details -- or just purely coincidence. As David pointed out, adding 'stuff' between the release and the log can cause string to really be destroyed and potentially reused. If you really want to know why this all works, you could read the objc runtime sources or crawl through the runtime's assembly as it executes. Some of it may have an explanation (runtime implementation details), and some of it is purely coincidence.
Doing things to a released object is an undefined behavior. Meaning - sometimes you get away with it, sometimes it crashes, sometimes it crashes a minute later in a completely different spot, sometimes a variable ten files away gets mysteriously modified.
To catch those issues, use the NSZombie technique. Look it up. That, and some coding discipline.
This time, you got away because the freed up memory hasn't been overwritten by anything yet. The memory that string points at still contains the bytes of a string object with the right length. Some time later, something else will be there, or the memory address won't be valid anymore. And there's no telling when this happens.
Sending messages to nil objects is, however, legitimate. That's a defined behavior in Objective C, in fact - nothing happens, 0 or nil is returned.
Update:
Ok. I'm tired and didn't read your question carefully enough.
The reason you are not crashing is pure luck. At first I though that you were using initWithString: in which case all the answers (including my original one (below)) about string literals would be valid.
What I mean by "pure luck"
The reason this works is just that the object is released but your pointer still points to where it used to be and the memory is not overwritten before you read it again. So when you access the variable you read from the untouched memory which means that you get a valid object back. Doing the above is VERY dangerous and will eventually cause a crash in the future!
If you start creating more object in between the release and the log then there is a chance that one of them will use the same memory as your string had and then you would crash when trying to read the old memory.
It is even so fragile that calling log twice in a row will cause a crash.
Original answer:
String literals never get released!
Take a look at my answer for this question for a description of why this is.
This answer also has a good explanation.
One possible explanation: You're superfluously dynamically allocating a string instead of just using the constant. Probably Cocoa already knows that's just a waste of memory (if you're not creating a mutable string), so it maybe releases the allocated object and returns the constant string instead. And on a constant string, release and retain have no effect.
To prove this, it's worth comparing the returned pointer to the constant string itself:
int main()
{
NSString *s = #"Hello World!";
NSString *t = [[NSString alloc] initWithFormat:s];
if (s == t)
NSLog(#"Strings are the same");
else
NSLog(#"Not the same; another instance was allocated");
return 0;
}

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.

stringWithContentsOfFile and initWithContentsOfFile return null after several runs

I am creating an iOS app which reads in a text file and displays the contents in a UIText field.
For the 1st three consecutive runs of thee app (Restarting a new session without exiting),
the data is read in fine. However on the fourth attempt, the data returned from the file is all nulls.
I've verified the file integrity. The issue exists when using stringWithContentsOfFile or initWithContentsOfFile.
After many hours of troubleshooting, I believe the issue is somehow related to a buffer being cleared within the above mentioned methods.
Any insight regarding this issue is greatly appreciated. I've tried many things with no luck.
Here's the code I use to read in the file:
TheString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"My_TextFile" ofType:#"txt"] encoding:NSUTF8StringEncoding error:NULL];
Here's the code I use to display certain contents of the file (The contents are placed in an array of type NSArray):
NSArray *My_Array;
My_Array= [TheString componentsSeparatedByString:#"\n"];
/* Obtain specific data to display */
DisplayedData = [My_Array objectAtIndex:M[l]-1];
:
:
/* Display the data in the view */
MyUITextView.text = DisplayedData;
/* Log the data */
NSLog(#"%#", MyUITextView.text);
On the 4th invocation of the code above, the data returned is blank and NSLOG is returning nulls
Thanks so much for any help!
Maybe I'm a little bit late with answer, but, anyway, maybe somebody will find it useful.
OK, I have also spent a day trying to figure out why my custom class for scrollable view is working 3 times and refuse at the 4-th time... I found that the problem has quite the same attributes as yours: nested NSString objects unexpectedly disappear. Though pointers point to the same address in memory, memory is already filled with quite arbitrary objects instead my NSStrings.
And I paid attention that I created these NSStrings using the following class method:
+ (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error
So, I'm not the owner of these NSStrings.
And I assumed that to be the owner can be a solution, so I created my NSStrings through alloc and
- (id)initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error
instance method.
App was repaired!

Resources