greetings Cocoa masters - this simple issue has me crawling the walls. I have a custom class called Movie which consists of a bunch of properties and a few collections. I am populating it successfully using FMDB and SQLite. However, with each pass through the result collection from the DB, my addObject: seems to be writing over the entire array:
SciFiLib = [[NSMutableArray alloc]init];
FMResultSet *SciFiResultSet = [db executeQuery:#"select Movie.*......];
Movie *m = [[Movie alloc] init];
while ([SciFiResultSet next]) {
m.movieID =[SciFiResultSet stringForColumn:#"movie_id"];
m.title = [SciFiResultSet stringForColumn:#"title"];
.....
[SciFiLib addObject: m];
At this point - I have NSLog'd the output of m - and it contains a different movie (title, ID, release date, etc. - so I know the data is OK). However, starting with the 2nd pass through the WHILE loop each subsequent addObject replaces the entire array with copies of the next data item. So at the end of my loop, I have 6 copies of the same movie data.
I have mirrored my custom class here with just an array of the movie titles, and that seemed to work, but I'd like to collect all of the properties of the movie(s) for my Model data. Can anyone shed some light on what might be causing this behavior?
Thanks in advance for your help and advice!
...
I think it's because you're just adding a pointer, and then reapplying the data to the same object "m". Make "m" inside the loop and release it so it gets remade each time.
Related
When I click a button, a UIAlertView prompts the user to type a name. This name is then created as a new 'Customer' object and inserted into a mutable array.
There is a separate mutable array called 'CustListByName', which stores a list of all names.
The problem im having is that when adding a second or third name, the app crashes. Sometimes it happens on the second try, other times on the third try. There is no information given in the debugger except for (lldb). The program reports EXC_BAD_ACCESS and then it dumps me to a screen with a bunch of assembly code.
The crash is happening in these lines of code:
Essentially, it clears the array of names and then repopulates it based upon the object array. I've studied in step by step with a breakpoint but everything seems correct up until the point of crash. It is also confusing why this happens on the second or third try, never the first.
[custListByName removeAllObjects];
for (Customer *object in custListByObject) {
[custListByName addObject:object->name];
}
Here is the code where a customer is created and inserted everytime the new customer button is clicked:
Customer *tempCust = [[Customer alloc] init];
tempCust->name =[[alertView textFieldAtIndex:0] text];
[custListByObject addObject:tempCust];
[tempCust release];
I would really appreciate help with this, thanks!
What I suspect is happening is that the UIPickerView is attempting to load a row using information from your customer array after you have already cleared it, and before you repopulate it. This would cause a bad access error.
What you may consider doing instead, is keeping two arrays, an NSMutableArray for loading the customers, and an NSArray as the actual data source for the UIPickerView. Then right before you reload the UIPickerView, you say:
dataSourceArray = [loadingArray copy];
[pickView reloadAllComponents];
Hopefully this helps.
Edit:
Here's what your updated code would look like if your loading array was called loadingCustListByName:
[loadingCustListByName removeAllObjects];
for (Customer *object in custListByObject) {
[loadingCustListByName addObject:object->name];
}
custListByName = [loadingCustListByName copy];
[pickView reloadAllComponents];
Doing this will ensure that the UIPickerView's datasource array always matches up with the number of rows it thinks it has.
I'm currently using a singleton as a data store for my app. I essentially store a number of events that are pulled and parsed from a web service and then added as needed. Each time I make a request from the web service, I parse the results and see if the items already exist. If they do, I delete them and add the updated version provided by the web service.
Everything appeared to be working properly until I fired up the Instruments panel to find out that my system is leaking the objects every time it loads them from the web service (from the second time on). The core method where things appear to be messing up is this one, which is located in my HollerStore singleton class:
- (void)addHoller: (Holler *)h
{
//Take a holler, check to see if one like it already exists
int i = 0;
NSArray *theHollers = [NSArray arrayWithArray:allHollers];
for( Holler *th in theHollers )
{
if( [[th hollerId]isEqualToString:[h hollerId]] )
{
NSLog(#"Removing holler at index %i", i);
[allHollers removeObjectAtIndex:i];
}
i++;
}
[allHollers addObject:h];
}
Quick explanation: I decided to copy the allHollers NSMutableArray into theHollers because it's being updated asynchronously by NSURLConnection. If I update it directly, it results in a crash. As such, I switched to this model hoping to solve the problem, however the Instruments panel is telling me that my objects are leaking. All the counts are exactly the # of items I have in my data set.
From what I can tell removeObjectAtIndex isn't effectively removing the items. Would love to get the thoughts of anybody else out there on three things:
Is my analysis correct that something else must be retaining the individual hollers being added?
Should I be using CoreData or SQLite for storing information pulled from the web service?
Do you know how long data stored in a Singleton should be available for? Until the app is killed?
Update
I think I've found the source, however perhaps someone can provide some clarity on the proper way to do this. I've created a method called parseHoller which takes a dictionary object created through SBJSON and returns my own model (Holler). Here are the last couple lines:
Holler *h = [[[Holler alloc] initFromApiResponse:hollerId
creatorId:creatorId
creatorName:creatorName
creatorImageUrl:creatorImage
comments:comments
attendees:attendees
wishes:wishes
invitees:invites
createdAt:createdAt
text:text
title:title
when:when]autorelease];
//(some other autorelease stuff is here to clean up the internal method)
return h;
I figured that since I'm returning an autoreleased object, this should be fine. Do you see anything wrong with this?
Have you tried to do a retain count on the objects that is leaking? Maybe that could clear up when or where it is being retained.
The code should be
[putObjectHere retainCount];
and then write to an NSLog
Hope it gives you something
Peter
I'm parsing data from a remote database to an iphone application and populating the table view. The program is able to populate the table view fine however its all the records. To sort the records i was advised to use NSPredicate to sort the data (which works) but when it tries to load the data, the program exists with no warnings in the console. I dont understand why, could someone have a quick glance over the code and tell me where im going wrong? (i think its something to do with these lines return [rows count]; and NSDictionary *dict = [rows objectAtIndex: indexPath.row];). Thanks for any help on this ...
In your viewDidLoad, retain the array and try.
rows = [[courses filteredArrayUsingPredicate:predicate] retain];
You should retain the rows array since filteredArrayUsingPredicate should return an autorelease object.
I have an application that downloads an xml file, parses the file, and creates core data objects while doing so. In the parse code I have a function called 'emptydatacontext' that removes all items from Core Data before creating replacements items from the xml data. This method looks like this:
-(void) emptyDataContext
{
NSFetchRequest * allCon = [[NSFetchRequest alloc] init];
[allCon setEntity:[NSEntityDescription entityForName:#"Condition" inManagedObjectContext:managedObjectContext]];
NSError * error = nil;
NSArray * conditions = [managedObjectContext executeFetchRequest:allCon error:&error];
DebugLog(#"ERROR: %#",error);
DebugLog(#"RETRIEVED: %#", conditions);
[allCon release];
for (NSManagedObject * condition in conditions) {
[managedObjectContext deleteObject:condition];
}
// Update the data model effectivly removing the objects we removed above.
//NSError *error;
if (![managedObjectContext save:&error]) {
DebugLog(#"%#", [error domain]);
}
}
The first time this runs it deletes all objects and functions as it should - creating new objects from the xml file. I created a 'update' button that starts the exact same process of retrieving the file the proceeding with the parse & build. All is well until its time to delete the core data objects. This 'deleteObject' call creates a "EXC_BAD_ACCESS" error each time. This only happens on the second time through.
Captured errors return null. If I log the 'conditions' array I get a list of NSManagedObjects on the first run. On the second this log request causes a crash exactly as the deleteObject call does.
I have a feeling it is something very simple I'm missing or not doing correctly to cause this behavior. The data works great on my tableviews - its only when trying to update I get the crashes.
I have spent days & days on this trying numerous alternative methods. Whats left of my hair is falling out. I'd be willing to ante up some cash for anyone willing to look at my code and see what I'm doing wrong. Just need to get past this hurdle.
Thanks in advance for the help!
Did you save the context after removing the objects in the for loop? Be aware that deleteObject: does not delete the object immediately, it simply schedule it for deletion when changes are committed, i.e., when you save the context.
EDIT: Your problem may be related to how you present your data to the user on your table view. Without additional code is difficult to tell exactly (are you using NSFetchedResultsController or not?), but my guess is that the interaction between deleting the data and showing them on the table is not correct. Probably, what is happening is that your table is told to visualize your data, but then, when you delete them, you are not updating correctly the table.
Wow, so after a few days of testing I went down a path that led me to tracking down Zombies & memory mgmt. This was a situation where the errors I was recieving were leftover from issues in another area. When parsing the data and placing it into Core Data I released an object that was to be autoreleased. So any subsequent calls to that item within core data (save, deletec, etc) caused a crash. Thank you all for your help and I vow to pay closer attention with my memory mgmt calls.
I'm working on a piece of code for an iPhone application that fetches a bunch of data from a server and builds objects from it on the client. It ends up creating roughly 40,000 objects. They aren't displayed to the user, I just need to create instances of NSManagedObject and store them to persistent storage.
Am I wrong in thinking that the only way to do this is to create a single object, then save the context? is it best to create the objects all at once, then somehow save them to the context after they're created and stored in some set or array? If so, can one show some example code for how this is done or point me in the direction to code where this is done?
The objects themselves are relatively straight forward models with string or integer attributes and don't contain any complex relationships.
In any case, don't save after inserting every object, or be prepared for dreadful performances.
Here is the code I use to populate a Core Data repository upon first launch.
#define MAX_UNSAVED_AIRPORTS_BEFORE_SAVE 1000
int numAirports = 0;
int numUnsavedAirports = MAX_UNSAVED_AIRPORTS_BEFORE_SAVE; // *** bug. see below
for (NSDictionary *anAirport in initialAirports) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Airport *newAirport = [NSEntityDescription insertNewObjectForEntityForName:#"Airport" inManagedObjectContext:managedObjectContext];
newAirport.city = [anAirport objectForKey:#"city"];
newAirport.code = [anAirport objectForKey:#"code"];
newAirport.name = [anAirport objectForKey:#"name"];
newAirport.country_name = [anAirport objectForKey:#"country_name"];
newAirport.latitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"latitude"] doubleValue]];
newAirport.longitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"longitude"] doubleValue]];
newAirport.altitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"altitude"] doubleValue]];
numAirports++;
numUnsavedAirports++;
if (numUnsavedAirports >= MAX_UNSAVED_AIRPORTS_BEFORE_SAVE) {
if (![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
numUnsavedAirports = 0;
}
[pool release];
}
Also don't forget to save one last time after the loop.
Also be aware that a bug exists that will lead to a crash if all three of the following conditions are met:
The Repository is empty
You have a UITableView with sections
Your first save saves more than one object.
The workaround in the code above is to initialize the numUnsavedAirports to MAX_UNSAVED_AIRPORTS_BEFORE_SAVE in order to make sure the first save happens after the first insert.
I hope this helps.
Saving after each object would produce very bad performance. You should have a balance of the saves perhaps every 100 (testing will determine the sweet spot) and then keep track of where you are at in the processing when the user quits.
You get time on exit to store state so you can easily store your position in the data processing (5 blocks of 100 saved) and pick back up where you left off.
Saving every object individually would hammer the disk and slow the app to a crawl.
It's probably better to create a single object and save the context.
You have 40k objects. Let's say that creating a single NSManagedObject takes x time units. 40kx time units is probably measurable. While the object creation is happening, the user may quit the app for some reason; users are unpredictable. The next time your app starts, you go through the process all over again. It would not be desirable to create the 39,999th object only to have the user quit the app and lose all that work.
If your app were to create each object and save, you could speed up this process a bit. The app starts up and checks to see if it was able to complete the task the last time it ran. If the task was incomplete, it could try to pick up where it left off.
The single object creation and save method may take a longer time to complete but will have a greater likelihood of completing the task.
In terms of memory consumption, this also minimizes the in memory state of your app. The context isn't tracking 40k objects in memory.