Core Data Migration loses NSNumber value - objective-c

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.

Related

Different instances of NSDate pointing to the same allocated memory?

I'm creating tests where I have to make sure 2 different NSDate instances are really two different instances of allocated memory. So I have this example code:
NSDate *date1 = [NSDate date];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:0 sinceDate:date1];
XCTAssertEqualObjects(date1, date2);
XCTAssertNotEqual(date1, date2);
The first assert should compare object values using "isEqual", and it's working great!
The second assert should compare pointers using "==". The bizarre thing is that it sometimes fails randomly, telling me that both pointers have the same value (ie, they are pointing to the same allocated memory).
As I'm allocating twice, it is supposed to be different memory areas... So why do I have this test failing randomly sometimes? Maybe XCode is reusing memory areas somehow?
You can't reliably force the creation of separate objects. Some classes may use tagged pointers. The set of classes doing that can change over time with releases of the OS. A tagged pointer really just encodes the value of the object into a pointer-sized value. It doesn't allocate any memory. By definition, any two objects represented as tagged pointers whose values are equal will have equal "addresses".
Also, an init method is just a method. It can return any object it wants. There's no rule that it has to return the receiver. It can release the alloced object it is sent to (self) and return a different object. If it can determine that an existing object (such as the parameter you're passing to -initWithTimeInterval:sinceDate:) meets its needs, it may return that object (with an extra retain). This sort of thing is common in immutable value classes, like NSDate or NSString.
You're going to have to reconsider your supposed need to "make sure 2 different NSDate instances are really two different instances of allocated memory".

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.

NSNumbers being added as values to NSMutableDictionary become nulls

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.

Re-initialize NSMutableArray as NSMutableArray

I was having a problem with my app throwing an exception when calling removeObjectAtIndex on an NSMutableArray, saying that myLocationsArray was declared immutable. All other manipulation on that array was fine, it was most definitely declared correctly etc etc but somewhere in my app it was getting set as immutable. After investigating for a while trying to find where it was getting set immutable, I decided screw it and just redeclared the variable as such:
myLocationsArray = [[NSMutableArray alloc] initWithArray:[defaults
objectForKey:MYLOCATIONSARRAY_KEY]];
right before the removeObjectAtIndex call.
However I know this has got to be badwrong, I'm calling alloc/init twice on the same variable. However it's the only thing that has worked. Is there any way to remind this variable that it is an NSMutableArray without introducing memory leaks like I am?
NSUserDefaults returns immutable copy of your array. It doesn't matter whether you put NSArray or NSMutableArray in it, it always give you immutable copy back.
So, do this to get a mutable copy that you can work with
myLocationsArray = [[NSMutableArray alloc] initWithArray:[[[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy] autorelease]];
or just this
myLocationsArray = [[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy];
I would suggest to set a breakpoint on the line where your program is throwing an exception (the one containing removeObjectAtIndex) and inspect with the debugger the real type of the array. If you go with you mouse over the array name, a popup menu will display giving you all the information you need about the pointed object.
What I expect is that you find out this way that the object is an NSArray (vs. NSMutableArray) and then trace back to the point where you initialized it in the first place.
It looks like you're working with NSUserDefaults. All objects you get out of NSUserDefaults are always immutable, regardless of what you stored into it. NSUserDefaults doesn't keep a reference to the specific object you set into it, it keeps the data. It's effectively making a copy. When you get something out of NSUserDefaults, it makes a new (immutable) object from the data it has stored and gives that to you.
Unsurprisingly, you can't change what's stored in NSUserDefaults by mutating what you (think you) stored in it. You can only change what's stored by replacing what you previously stored by storing something anew.
The declaration should not matter; your error is a run-time error. It sounds like your myLocationsArray variable has been assigned an immutable array (NSArray) though whether it is being re-assigned somewhere or was always immutable is impossible to say from your code fragment.

how to initialize an object(NSObject subclass) at a specific address

Hi I need to initialize an NSObject at a particular location that I specify(through a void* pointer, for example). For a little bit of context, I am writing a sqlite3 aggregate function. To keep temporary results from this function, I have to call a sqlite3_aggregate_context function, which allocates a block of memory for me. I want to store an NSDecimalNumber at this location.
So far I have tried two approaches:
1)allocWithZone, by doing:
void *location = sqlite3_aggregate_context(...); //returns a block of allocated memory of a certain size
NSDecimalNumber *num = [[NSDecimalNumber allocWithZone:NSZoneFromPointer(location)] initWithInt:0];
This does not work because NSZoneFromPointer returns nil. Docs say that the arguments to this function must be a previously allocated pointer, which it is. I dont know if this means allocated using NSZoneMalloc/Calloc.
2)
id location = sqlite3_aggregate_function(...);
location = [[NSDecimalNumber alloc] init];
but this causes some kind of infinite recursion when freeing the memory...not sure what the deal is. A screenshot here:
http://dl.dropbox.com/u/3002073/Public%20Sync/sqlitefunctionissue.png
Any suggestions will be greatly appreciated!
You can't really determine reliably where an object is going to be created in memory. The NSZoneFromPointer fails for you because the sqlite3 API is not using zones to allocate its resources.
If you want to be able to pass a specific location, you should do so using a pointer to the object (so you are storing a pointer to a pointer basically). You can then read this information from your aggregate function and update it accordingly. Just make sure that you don't simply let your object be freed at the end of the call without taking care to release it (or you'll have a leak).
So, for example, you could do something like:
NSDecimalNumber** numberLocation = sqlite3_aggregate_context(...);
*numberLocation = [[NSDecimalNumber alloc] initWithDouble:25.0];
You now have a reference to your object stored in your special memory area and can access it any time:
NSDecimalNumber* storedNumber = *numberLocation;
NSDecimalNumber* computedNumber = [[NSDecimalNumber alloc] initWithDouble:[storedNumber doubleValue] * someComputation];
[storedNumber autorelease];
*numberLocation = computedNumber;
On the other hand, I agree with Mark; maybe this immutable class isn't the best solution to your problem?
Your first version is simply not going to work. NSZoneFromPointer only works when passed a pointer allocated from a zone. It's used so you can allocate an object from the same zone as some other object.
The second version ought to work, though it's difficult to tell without more context. What are you passing to sqlite3_aggregate_context as the size of the memory to allocate? And how are you freeing that memory when you're done?
The second version doesn't work because the "id" type is actually a pointer, so you're pointing it at the memory returned by sqlite3_aggregate_context(), then pointing it at the memory returned by alloc/init. You really need to store a pointer-to-pointer to get that to work the way you want.
NSDecimalNumber is an immutable class, so calling -init on it (as opposed to -initWithDecimal:) is just going to get you some default value. What sort of code are you using to replace the NSNumber with new values as the function progresses?
More to the point, why use NSDecimalNumber at all, as opposed to a C integer, or double, or whatever?