NSNumbers being added as values to NSMutableDictionary become nulls - objective-c

I have a piece of code that constructs a dictionary of objects as keys and operation codes (like add/update/remove) as values.
Recently, either with iOS 6, or with the arrival of "zee Germans", it broke. Values are constructed, and occasionally added, but most of the time I get a dictionary full of (null) values.
for (int i = 0; i < newArray.count; i++)
{
if (![oldArray containsObject:[newArray objectAtIndex:i]])
{
NSNumber *opNumber = [NSNumber numberWithInt:(int)kInsertOperation];
[_operationsForRows setObject:opNumber forKey:[newArray objectAtIndex:i]];
}
else ...
}
I've already decorated the code with intermediate variables and explicit casting of enum value to int, and grew even more confused. Printing out the resulting dictionary gives:
{
"<Order: 0x8aad980>" = (null);
"<Order: 0x8aad7a0>" = (null);
}
Can anyone explain, what am I missing here? The dictionary shouldn't even be able to hold nulls, should it?
This code is under ARC.
NSMutableDictionary was initialized on the main thread, but used exclusively on a single secondary thread.
Sometimes, an object or two would be added correctly. Say, 10%. What I've tried since:
I've moved dictionary initialization to the same thread as usage. Wow, 90-95% of objects are added correctly, but still a null here and there.
I've saved all possible NSNumbers in class fields. With both dictionary and numbers created on main thread, 0% success rate, lower than I started with.
Using current-thread-initialized dictionary and main-thread numbers in class fields, I get to 90-95% again...
Cry, cry... Looks like a rewrite of the whole place.

Try using NSMutableDictionary instead of NSDictionary.
The difference between the two being that you can modify NSMutableDictionary after initialisation whereas NSDictionary is set.

Related

NSMutableArray will not return/keep a sequence of NSNumbers

I would like, every time this function is called, that the NSMutableArray "sequence" to return the numbers already in the sequence, plus another random one from 0 to 3 at the end. This is the function I have so far:
Edit
I tried calling initialising this function from inside didMoveToView and it now returns a sequence.
This code works now:
-(NSMutableArray *)extendSequence {
int newItem = arc4random_uniform(4);
NSNumber* newItemAsNumber = [NSNumber numberWithInteger:newItem];
[sequence addObject:newItemAsNumber];
NSLog(#"%#", sequence);
return sequence;
}
(I am not sure where/if I could insert the NSMutableArray *sequence = [NSMutableArray alloc] init]]; statement yet, because when I tried earlier, it wiped the sequence each time it was called.)
How could I achieve this?
A random sequence could return to be something like e.g. "1", then "1,3" then "1,3,0" etc.
Thank you in advance.
Sounds like you are a little unclear on where you are keeping track of the "state" of what the sequence is so far. It sounds like you have an instance variable called "sequence" on this object somewhere (otherwise what you are writing now wouldn't compile). This is a reasonable place to keep track of this set of numbers, which will exist throughout the object's lifecycle, but adding to it each time the -sequence method is called.
You should add the initialization line for the array to your object's -init method. That way it is initialized just once with the object.
(The reason the current code returns nil is that you're never initializing the array, so the "additions" to it have no effect. The reason your other version, where you initialize within that method, kept wiping out the contents, is simply that you were creating and returning a fresh array every single time, which would replace the one you'd created the last time.)

Changing NSMutableArray size in objective-c

I was looking around and couldn't find anything, and I'm starting to think it's not possible with objective-c.
I have a NSMutableArray *myMutableArray and the size varies depending on what csv file is loaded. Since I do not set a size of myMutableArray I can't do:
if (c == 5){
myMutableArray[q] = [[NSNumber numberWithFloat:myOtherArray] stringValue];
q = q + 1;
c = 0;
}
Else {
c = c + 1;
}
Since myMutableArray is technically of size nil I guess I can't add objects to it.
In cases, q can be between 1500 and 2500.
My question is, how do I make `myMutableArray' change size on every loop.
If this isn't possible, I guess I will have to make myMutableArray very large - but I need the values in myMutableArray for a graph. If I do myMutableArray = [[NSMutableArray alloc] initWithCapacity:5000]; and don't use the 5000 memory locations, will these locations be nil, or 0? (sorry if the technical words are wrong, by memory locations I mean the memory given to myMutableArray)
Thank you, if there is anything else I can add to this please feel free to let me know.
EDIT: What I'm trying to achieve is adding data to the array, and with that data create a graph
You can't have a sporadically populated array. Creating an array with capacity 5000 is just a hint as to how much data you might be going to store into the array. Consider using an array of dictionaries where the dictionary contains the q value (presumably one coordinate) and the associated value. You can then sort the array based on the q values if you need to (for plotting). Then you can just add the dictionaries to the array as usual (addObject:).
The NSMutableArray class declares the programmatic interface to objects that manage a modifiable array of objects. This class adds insertion and deletion operations to the basic array-handling behavior inherited from NSArray.
If you
arrayWithCapacity:
Creates and returns an NSMutableArray object with enough allocated memory to initially hold a given number of objects.
Mutable arrays expand as needed. When declaring them, we can init them like this:
+ (instancetype)arrayWithCapacity:(NSUInteger)numItems
Here numItems simply establishes the object’s initial capacity.
Later to add more data, i.e. to expand mutable array, use this
addObject:
What it does is, it inserts a given object at the end of the mutable array.
- (void)addObject:(id)anObject
It's important to note that:
The object to add to the end of the array's content. This value must not be nil. It raises an NSInvalidArgumentException if anObject is nil.

Why do we create instances of classes in for loops?

So I am busy reading an objective-c book by Big Nerd Ranch. I'm on chapter 17 at the moment and managed to complete the required challenge at the end of the chapter. However, I just have two question that I would like to understand.
In the following bit of code - StockHolding is a custom class that has instance variables and the stocks (an array) points to three instances of stockholding with values setting its stock value and cost in dollars.
At first I tried to access the array to get the data from the objects it pointed to - but it seems that was not going to work as the array doesn't know what data its objects contain - just where they are in memory, right?
What I want to know is why was it necessary to create a new instance of stockholding (holdings) in this for loop to access those variables?
How does the new instance of stockholding know what the values of my stocks are?
for (StockHolding *holdings in stocks){
NSLog (# "%# has %d shares. Cost: $%.2f. Stock value: $%.2f", [holdings stockName],[holdings numberOfShares], [holdings costInDollars], [holdings valueInDollars]);
}
I'm going to try have a guess here to see if maybe I understand it a little better?
We create an instance of our class in the for loop so that we have access to its instance methods and variables - then we use the stocks array to get the variables from those objects in the array?
I may be completely off.. :(
Any advice?
stocks is an array having the objects of type StockHolding
So in order to access all values in the array and print the values.You need to get all the StockHolding instance inside the array we use for ...in method
note Here new instance is not created just new reference is made to the memory that is in the array so that you can access it and use it
Absolutely no new instances are created in the for loop at all. Since Objective-C objects are always represented as pointers, one variable != one instance. The holdings local variable inside the loop is assigned the pointer to the element of the array which is currently being enumerated upon each iteration. It's just a "reference" to an already existing object.
You're not creating new instances. You're iterating through existing instances.
Presumably in [CODE] you have created the objects and added them to the NSArray. The for loop just gives them to you one at a time. You name it holdings, do something with it, then grab the next.
That's all.
In Objective-C objects are typeless. Any message can be sent to any object. Code like [holdings stockName] means "send the message 'stockName' to the object 'holdings'". So the Objective-C runtime will inspect the object to see whether it implements that message. If so then it'll pass execution into the implementation.
The type of your object makes no difference to how processing will occur at runtime.
An NSArray stores anything that conforms to the NSObject protocol. So it can hold any old mix of objects. The same goes for the other collections.
Although you could write all your code without mentioning a single object type, you usually don't because if you say which type of objects you're dealing with then the compiler can perform some sanity checks. That makes you less likely to write broken code.
So the code:
for (StockHolding *holdings in stocks)
just means "let me do something to every object in the collection stocks and don't give me any compiler warnings when I treat them like instances of StockHolding". They may actually be other classes. If they're other classes that implement stockName, numberOfShares and the rest then your code will work perfectly.
So, for example:
NSMutableArray *arrayOfStrings = [NSMutableArray array];
[arrayOfStrings addObject:#"34.3"];
[arrayOfStrings addObject:#"19.8"];
float total;
for(NSNumber *number in arrayOfStrings)
{
total += [number floatValue];
}
Will compile and work perfectly — not because the strings are actually converted to numbers but because both classes implement floatValue to return a float. So each NSNumber *number is actually an NSString, and if you tried to call, say, isEqualToNumber: on any of them you'd raise an exception because that isn't implemented by strings. But telling the compiler you're going to act as if they're numbers means you don't get a warning for using floatValue and when the runtime spots that the object implements floatValue execution continues as usual.
The for..in loop is used for fast enumeration.
This
for (StockHolding *holdings in stocks)
{
}
won't create any new object, it takes one object from array and cast it to the specified type and assign it to the specified variable.
Means:
Takes the object from the array . Equivalent to [stocks objectAtIndex:index];
Assign it to the specified object. Equivalent to StockHolding *holdings = [stocks objectAtIndex:index];
Note that Only the reference is used (assignment) there is no object is allocated.

Core Data Migration loses NSNumber value

After migration, one of my numerical values that should be non-zero now appears as zero. If I don't migrate, the value retains its non-zero value.
I first tried to set a number value in a managed object like this:
[temp setNumUses:temp.numUses+1];
... but that caused an'EXC_BAD_ACCESS' so I changed it to:
int hold = ((int)[[temp valueForKey:#"numUses"] intValue]);
hold++;
[temp setNumUses:[[NSNumber alloc] initWithInt:hold]];
... but after migration, this code claimed that hold was initialized as an int with a value of 0 when before running the new code its value was clearly 1 or more (referring to the test object which was used only 1 time).
When I do not migrate the Core Data Database the NSNumber retains its value fine through many context saves and application terminations.
What might I be missing? I have some extensive code that modifies values of changed NSManagedObjects in a database stored elsewhere but none of it tampers with 'numUses'.
Thoughts?
The simplest explanation is that the migration somehow convinces Core Data that numUses is a new attribute with default value of zero. The reason for this would be in the migration model and not in the assignment code you have provided.
However, the code in the parent does suggest you don't quite understand NSNumber and miss using NSNumber elsewhere might cause your problem.
NSNumber is only an object wrapper around numerical values. You can't perform operations with it. That is why this line:
[temp setNumUses:temp.numUses+1];
... causes the EXC_BAD_ACCESS. As you discovered, you have to convert a NSNumber to an int or NSDecimalNumber to perform mathematical operations on it.
This line:
[temp setNumUses:[[NSNumber alloc] initWithInt:hold]];
... will leak memory. You initialize a NSNumber object but never release it. In this case (and the vast majority of all cases), you should use the class methods to create NSNumbers because they return an autoreleased object that won't leak. So:
[temp setNumUses:[NSNumber numberWithInt:hold];
In general, it is bad practice to initialize any object in a method parameter. You are asking for nasty memory leaks that will be very hard to track down. The report of the leak may not show up in the code where the method is called but in the method itself.

Implementing an NSOutlineView to edit the contents of a plist file

I'm writing a game using cocos2d-iphone and our stages are defined in .plist files. However, the files are growing large - so I've developed an editor that adds some structure to the process and breaks most of the plist down into fixed fields. However, some elements still require plist editor type functionality, so I have implemented an NSOutlineView on the panels that show 'other parameters'. I am attempting to duplicate the functionality from XCode's 'Property List Editor'.
I've implemented the following system; http://www.stupendous.net/archives/2009/01/11/nsoutlineview-example/
This is very close to what I need, but there is a problem that I've spent most of today attempting to solve. Values for the key column are calculated 'backwards' from the selected item by finding the parent dictionary and using;
return [[parentObject allKeysForObject:item] objectAtIndex:0];
However, when there are multiple items with the same value within a given dictionary in the tree, this statement always returns the first item that has that value (it appears to compare the strings using isEqualToString: or hash values). This leads to the key column showing 'item1, item1, item1' instead of item1, item2, item3 (where items 1-3 all have value ''). I next tried;
-(NSString*)keyFromDictionary:(NSDictionary*)dict forItem:(id)item
{
for( uint i = 0; i < [[dict allKeys] count]; i++ ) {
id object = [dict objectForKey:[[dict allKeys] objectAtIndex:i]];
if ( &object == &item ) {
return [[dict allKeys] objectAtIndex:i];
}
}
return nil;
}
But this always returns nil. I was hoping that somebody with a bit more experience with NSOutlineView might be able to provide a better solution. While this problem only appears once in the linked example, I've had to use this a number of times when deleting items from dictionaries for instance. Any help would be greatly appreciated.
return [[parentObject allKeysForObject:item] objectAtIndex:0];
However, when there are multiple items with the same value within a given dictionary in the tree, this statement always returns the first item that has that value …
Well, yeah. That's what you told it to do: “Get me all the keys for this value; get me the first item in the array; return that”.
… this statement always returns the first item that has that value (it appears to compare the strings using isEqualToString: or hash values).
It's not that statement that's doing it; it's how dictionaries work: Each key can only be in the dictionary once and can only have exactly one object as its value, and this is enforced using the hash of the key and by sending the keys isEqual: messages (not the NSString-specific isEqualToString:—keys are not required to be strings*).
The values, on the other hand, are not uniquified. Any number of keys can have the same value. That's why going from values to keys—and especially to a key—is so problematic.
*Not in NSDictionary, anyway. When you attempt to generate the plist output, it will barf if the dictionary contains any non-string keys.
I next tried;
-(NSString*)keyFromDictionary:(NSDictionary*)dict forItem:(id)item
{
for( uint i = 0; i < [[dict allKeys] count]; i++ ) {
id object = [dict objectForKey:[[dict allKeys] objectAtIndex:i]];
if ( &object == &item ) {
return [[dict allKeys] objectAtIndex:i];
}
}
return nil;
}
But this always returns nil.
That's the least of that code's problems.
First, when iterating on an NSArray, you generally should not use indexes unless you absolutely need to. It's much cleaner to use fast enumeration.
Second, when you do need indexes into an NSArray, the correct type is NSUInteger. Don't mix and match types when you can help it.
Third, I don't know what you meant to do with the address-of operator there, but what you actually did was take the address of those two variables. Thus, you compared whether the local variable object is the same variable as the argument variable item. Since they're not the same variable, that test always returns false, which is why you never return an object—the only other exit point returns nil, so that's what always happens.
The problem with this code and the earlier one-liner is that you're attempting to go from a value to a single key, which is contrary to how dictionaries work: Only the keys are unique; any number of keys can have the same value.
You need to use something else as the items. Using the keys as the items would be one way; making a model object to represent each row would be another.
If you go the model-object route, don't forget to prevent multiple rows in the same virtual dictionary from having the same key. An NSMutableSet plus implementing hash and isEqual: would help with that.
You probably should also make the same change to your handling of arrays.
To clarify, I eventually resolved this problem by creating proxy objects for each of the collections in the plist file (so, for every NSMutableArray or NSMutableDictionary). This meant that I essentially mirrored the Plist structure and included references back to the original objects at each level. This allowed me to store the array index for each object or the dictionary key, so when saving items back from the outline view to the Plist structures, I used the 'key' or 'index' properties on the proxy object.